Skip to content

The JavaScript Gantt odyssey

June 14, 2012

An “extended” overview of available JavaScript Gantt components…. and more

 

I cant believe it; I did it again!

I promised, but can’t resist……. I should have known it!

 

But let’ start from the beginning.

O course all of you know very well (at least you should!) Teamwork, the wonderful project management software that Open Lab is developing since 2003, which is about to celebrate its fifth major release; finally I can confirm that we will be out at the end of June.

One of the major new features for version 5  is a full Gantt editor, browser based.

I was in charge to select a JavaScript component that we could embed in our product – in order to save time.

So this post is firstly a selection of available components, but …….. read until the end.
(no thanks! I’m in a hurry)

 

My ideal component should be based on  jQuery, be LGPL (or an equivalent free-free license), be easy to configure, skinnable, multi-browser.

Unlike in my previous articles, when I was looking for grid components, tag inputscalendar, JavaScript templatesconfirm etc., for Gantt editors the market offers few opportunities.

 

Here is what I found:

 

DHX Editable JavaScript Gantt Chart

image

This component is very easy to use, simple and well documented.

It supports drag&drop and edit.

In my case the problem was that this component is tightly linked with the DHTMLX library and this is not my choice.

But if you are not already “linked” to a “framework” (like jQuery) maybe this could be your choice.

Regarding licensing, its both under GPL or commercial license. A fee of $699 for an enterprise license may be worth it.

 

 

Brynthum EXT Gantt

image

I think this is the best component I found ever.

The user interface is smooth, responsive and well designed. It supports editing in place, zooming, buffering for large Gantt (what could be the meaning of a project with 1000 tasks? Winking smile ) drag&drop etc.

The documentation is well done and complete…… sigh! it is based on EXTJs!

The second negative point is the licensing that doesn’t fit our needs. Only a commercial license is provided.

But if this component fits you, give it a try as it is a great component.

 

A little sad about leaving EXT gannt, I found

jsGantt

image

this component developed by Shlomy Gantz and Brian Twidt seems a little bit “rude” when compared with the two above, but undoubtedly it has the value of  being built from scratch. No libraries! 100% sweat of the brow!

This component is written for data display, so if you want to drag&drop, edit, interact in some way, you must to write your code.

Documentation is…. concise, but complete.  The license model is BSD.

The lack of the editing ability convinced me to pass over.

 

jquery.ganttView

image

Finally a jQuery one!

As the name suggests it is only a viewer. Even the data model is a little weak, missing support for dependencies, but is one of the few components I found built upon jQuery. The project seems to be dying and the author Frank Grubbs did not release updates since 2 years ago.

There is an interesting work started from this project http://mbielanczuk.com/2011/06/jquery-gantt-chart/

but it still lacks some features.

 

I was scraping the bottom of the barrel, when I found this Japanese component

jquery.gantt

image

Done by Maro, this is a very light Gantt viewer; it doesn’t support dependencies, editing or other advanced features, but it can be used as a starting point.

 

I was really desperate at this point! What I was looking for was something like the component used on

Gantter

image

Gantter is an online service, so not a part of this family of components, but I used it to set my requirements.

They don’t sell their component.

 

But searching for something similar I found interesting stuff in the far east:

Sunflower 向日葵任务甘特图

image

If you are fluent with Chinese, maybe you’ll find working with this component easier than I did.

But Chinese apart this is a really full featured component, and I work with it for a while. It supports editing, drag&drop, dependencies, zooming, etc.

I didn’t understand the license type very well, but a free version seems available.

Documentation is available in Chinese only, and Google Translate can help you only partially.

It doesn’t use jQuery.

 

What I found really incredible is how Sunflower component is similar to the one used by the above online service…

 

And last but not least I found

 

TreeGrid Gantt Char

image

This component is awesome! really! it can do everything.

Supports every kind of  actions, notation, dependencies, colors, really everything.

There are lots of examples and the online documentation is complete and effective.

imageMaybe someone can argue that the readability of this kind of Gantt is not ideal, but the component can be used in a simpler way.

Scalability is not an issue at all for this component, there is an example with 1000000 rows and 1000 columns!

Also compatibility is wider than any marketer requirement could ever be (runs even on IE6).

 

Ok it is not based on jQuery, and the commercial license cannot fit every wallet, but a component like this requires years for developing it.

 

 

dojox.gantt

image

I have to cite the dojo library for completeness, but I didn’t play with this component.

Seems quite complete and the documentation is well done. Based on Dojo, not on jQuery.

 

Our solution

If you reached this point maybe you are asking what component did I choose?

I told you at the beginning:

 

I did it again!

I developed my own component!

 

Why?

  1. because none of the component mentioned above is LGPL/MIT
  2. none is built in jQuery
  3. I need to actively manage the data model because a Teamwork task/project’ one is more flexible than one representable with a Gantt

but mainly because I love to write code!

In this case the work seemed really hard so I  worked in pair with Silvia, and this is actually a nice experience by itself!

 

Here’s the result of our efforts:

image

Even if the layout is clean and may seem barebones it actually supports a lot of features:

  • in-place editing image
  • drag&drop
  • zooming
  • do/undo
  • multiple dependencies
  • full editing
  • dates shortcuts image
  • css skin
  • multiple browser
  • resources editing
  • multiple assignment
  • milestones
  • export data in JSON format
  • resize & scroll

and

  • tasks status!

the last point is the mayor difference introduced in Teamwork task management  model.

Teamwork focus is capturing work done in real time. To do this we built a tool that can model real time situations and easily change in time. This is in contrast with traditional project management methodology where projects are defined in advance in all details – not realistic for most work situations.
So Teamwork’s model of project, task trees, dates and task states is different from that of classical Gantts: tasks can be open beyond their due dates, task can be suspended inside their time scope, and so on. Also the automation due to dependencies has been somewhat simplified with respect to classical Gantt tools.

 

The introduction of task’s states lets our Gantt act as a sort of workflow.  Let’s see what happens when you close a phase with dependencies:

here the first phase is open (second row: green-> open)

image

 

here I closed the first phase (all subtasks become blue->closed : rows 3 and 4 ) so the second phase can start and become green->open and its dependants become orange->suspendedimage

 

 

image This is the task full editor

 

 

 

 

 

 

 

 

image

and the resource quick editor

 

 

 

 

 

 

 

 

You can try our component online here on http://gantt.twproject.com/

Source code is available on GitHub here. This component is release under MIT license.

 

In the next post I’ll describe in detail how to use the component code, stay tuned.

 

The complete link collection for this article is in a Licorize booklet: http://licorize.com/projects/rbicchierai/JS-Gantt-editor

Easy to confirm!

January 26, 2012

A jQuery confirm plugin

 

I’m re-writing some components for our  world-wide selling Teamwork, and I was in the need to have a nicer way to confirm some “dangerous” user actions.

The standard JavaScript solution is something like

if (confirm(“do you want to destroy everything?”)) 
  … <em>here the code for destroying</em> 

 

While the code is very simple, the popup alert is unfashionable and old-smelling.

The best solution I found is the Nadia Alramli’s jQuery confirm plugin that I have already used massively in Licorize.

The first great idea in the Nadia’s plugin is that the confirm question is shown exactly where you clicked for the action.

The second idea is that the “confirm” is just “applied after” your code behavior.

Let se the simplest example (from Nadias’ blog):

// The action.
$('a').click(function() {
  alert('click');
  return false;
});
 
// The most simple use.
$('a').confirm();

 

As you can see, first you bind the event normally, then you will apply the confirm.

This approach is very nice, but its is very “javascript oriented”; what I mean is that you need a js piece of code for setting up the confirm, and this is a little bit rigid for an inline usage.

Example:

<button onclick=”destroyAll()”>

 

where “destroyAll” is your function.

 

So I wrote this little jQuery plugin that can be used like this:

<button onclick=”$(this).confirm(destroyAll)>

Here the code:

 $.fn.confirm = function(action, message) {
    if (typeof(action) != "function")
      return;
    this.each(function() {
      var el = $(this);
      var div = $("<div>").addClass("confirmBox").
              html(message ? message : i18n.DO_YOU_CONFIRM);
      div.css({"min-width":el.outerWidth(),"min-height":el.outerHeight()});
      div.oneTime(5000, "autoHide", function() {
        $(this).fadeOut(100, function() {
          el.show();
          $(this).remove();
        });
      });
      var no = $("<span>").addClass("confirmNo")
              .html(i18n.NO).click(function() {
        $(this).parent().fadeOut(100, function() {
          el.show();
          $(this).remove();
        }).stopTime("autoHide");
      });
      var yes = $("<span>").addClass("confirmYes")
              .html(i18n.YES).click(function() {
        $(this).parent().fadeOut(100, function() {
          el.show().oneTime(1, "doaction", action);
          $(this).remove();
        }).stopTime("autoHide");
      });

      div.append("&nbsp;&nbsp;")
              .append(yes)
              .append("&nbsp;/&nbsp;")
              .append(no);
      el.hide().after(div);

    });

    return this;
  };

few lines of CSS for the sake of completeness:

.confirmBox{
    display:inline-block;
    z-index:10000;
    vertical-align:middle;
    text-align:center;
    font-size:larger;
    font-style:italic;
    color:#a0a0a0;
  }
  .confirmBox .confirmNo{
    color:#e06060;
    cursor:pointer;
    font-weight:bolder;
  }
  .confirmBox .confirmYes{
  color:#60e060;
  cursor:pointer;
    font-weight:bolder;
  }

and two lines for internationalization:

var i18n = {
    YES:"Yes",
    NO:"No",
    DO_YOU_CONFIRM:"Do you confirm?"
  };

 

See a Demo page here

This component is released under MIT license.

Your feedback will be appreciated.

To JSON or not to JSON

January 18, 2012

How to simply convert a sqlite database to JSON

Last week my collegue Matteo (aka pupunzi) was porting an iPad application (booo!) to HTML5 (yeah!).

The iPad application was a virtual museum that was using a sqlite dabatase for reading data relative to rooms, biography, history and so on.

Even if sqlite is internally supported by some browsers (e.g. Chrome) the easiest way to read data in javascript is to have a JSON object.

I wrote a simple java code to dump the whole database into a JSON object.

import net.sf.json.JSONObject;
import net.sf.json.JSONArray;
import java.sql.*;
import java.util.List;
import java.util.ArrayList;
import java.io.FileOutputStream;
public class DB2JSON {

  public static void main(String[] args) {

    if (args.length<1)
      return;
    Connection conn = null;

    try {
      String dbFile = args[0];
      JSONObject ret = new JSONObject();
      Class.forName("org.sqlite.JDBC");
      conn = DriverManager.getConnection("jdbc:sqlite:" + dbFile);
      Statement stat = conn.createStatement();

      ResultSet tables = stat.executeQuery("SELECT name 
         FROM sqlite_master 
         WHERE type='table' ORDER BY name;");
      List tableNames = new ArrayList();
      while (tables.next()) {
        tableNames.add(tables.getString("name"));
      }
      tables.close();

      for (String tableName : tableNames) {
        JSONArray jsa = new JSONArray();

        ResultSet rows = stat.executeQuery("select * from " + 
           tableName + ";");

        while (rows.next()) {
          JSONObject row = new JSONObject();
          ResultSetMetaData meta = rows.getMetaData();
          for (int i = 1; i <= meta.getColumnCount(); i++) {
            row.element(meta.getColumnName(i), rows.getObject(i));
          }
          jsa.add(row);
        }

        rows.close();
        ret.element(tableName, jsa);
      }

      conn.close();

      // result on console
      System.out.println(ret.toString());

      //result on file
      FileOutputStream fos = new FileOutputStream(dbFile+".json");
      fos.write(ret.toString().getBytes());
      fos.close();     

    } catch (Throwable e) {
      try {
        if (conn != null)
          conn.close();
      } catch (SQLException s) {
      }
      e.printStackTrace();
    }
  }
}

The resulting JSON object will have a property for each table containing an array of records. Different types of properties are supported (String, int, long, dates etc.)

There are two dependencies to external libraries:

  1.  http://json-lib.sourceforge.net/
  2. http://www.zentus.com/sqlitejdbc/

Enjoy.

P.S.: the pupunzi’s porting result is an HTML5 application that runs everywhere and it is looks even more beautiful than the iOS one Smile !

How to destroy an evil presence and live better

November 11, 2011

 

Yesterday while I was biking my dog  across Cascine park in Florence mumbling about a new product idea, my eyes was attracted by some strange orange points in the autumnal yellow carpets. I got off the bike to have a closer look. WOW! there was thousand of alkekengi fruits on the ground.

Alkekengi (Physalis alkekengi L.) is a plant from the same family of tomatoes, it is  invasive and an alien specimen in Italy, it is something negative!

Let me explain how to “eradicate” this evil presence:

1) pick-up some dozens of alkekengi fruits with their orange “lanterns”

20111106-IMG_0576

 

2) pop the lanterns and get the balls “naked”, but without removing the ”leaves”

20111106-IMG_0590 20111106-IMG_0594  20111106-IMG_0595 20111106-IMG_0593

3) prepare a “bain marie” for dark chocolate. If you don’t have the right pot you can stack a metal bowl on a boiling water pot

20111106-IMG_0586 20111106-IMG_0587

3) when chocolate melts, start dipping alkekengi balls one at a time.
Use folded leaves for holding them

20111106-IMG_0596 20111106-IMG_0597

 

4) then place the hot ball on a baking paper sheet to let them cooling down

20111106-IMG_0599

 

5) in the meanwhile get some hot chilies (I used real small ones that decide to happily colonize a lemon vase), smash them in a mortar and then add them to hot chocolate. Stir to amalgamate.

20111106-IMG_0600 20111106-IMG_0601 20111106-IMG_0602

 

6) now you are ready for the last chocolate passage.

20111106-IMG_0604

 

7) let chocolate solidify and eventually repeat last passage.

 

The chilled dark chocolate is in perfect balance with the sour flavor of alkekengi.

Now  you are ready to destroy this evil presence!

 

P.S.: I forgot for the rest of the week-end the fu$#£!…. idea on the new product.

Power of chocolate.

Tassellate: playing on CANVAS

February 10, 2011

I’ve enjoyed so much playing on HTML5 <CANVAS> that now I cannot stop…

image

(test demo here)

Now I’m playing on a component that I’d like to use as replacement for the current “weekly review” game on Licorize. If you are wondering about a “game” on a GTD operation like “weekly review”, have a look at Game mechanics for thinking users article by Pietro Polsinelli.

The goal is to transmit the idea of having something to be “cleared”; a picture covered with black pieces should work fine!

The requirement is that the number of pieces covering the image must be pre-determined.

First of all I need an algorithm to “tassellate” the image with random shaped triangles. Actually the triangles are not overlaying, so I divide recursively the space available.

Go have a simple algorithm, first I split the image in two triangles, then I apply to both the recursive splitting.

A single splitting step:image

1) choose a random side

2) find a point (x) on that side, nearly in the middle

3) link the point (x) with the opposite corner and generate two triangles

4) apply splitting on both triangles

here is the code:

image

Then the recursive function: notice that I have to use a queue to “balance” the recursion on both splitted parts, otherwise I will divide only the first triangle reaching the exit condition (x pieces reaced).

image

Then I used my simple Stage object to manage canvas interactions (read lenscape a canvas interaction tutorial for details) as click on triangles, mouse over, etc…

I’ve also added a sound on “remove piece” button, but dealing with the <AUDIO> tag is another history…

Lenscape, a canvas interaction tutorial

February 4, 2011

Recently I started playing with the HTML5 <CANVAS> object sometimes having fun and sometimes feeling pain. I would like to share here my pains with your all 🙂

image

(see example and bookmarks)

I approached html drawing far in the past when svg was a weak light in the darkness of browser graphics. Mainly due to the lack of IE support for svg at the time I chose to create my own graphic library emulating  pixel drawing with thousands of lilliputian <div>.

In the following years I had only sparse contacts with html graphics but always having in mind the svg approach, where a line, a circle or whatever is a solid xml tag.

e.g: <ellipse cx=”300″ cy=”150″ rx=”200″ ry=”80″ style=”fill:rgb(200,100,50);”/>

this is really “natural” for me, html-writer: every single element has its own tag, and we are all happy!

Last month here, in Open Lab, we decided to start developing our first game (first for the company but not for the people working in it) and we decided to develop it using HTML5!

It’s time for me to resurrect dormant skills, and have a look at what is happening in the world of html gaming.

Surprise! Nobody is using svg for gaming, the main road is deeply tracked through <canvas> fields;  so then I started studying it.

First of all I got a punch in the stomach learning that a canvas is, as the word says, only a place where to “draw” something, not a place where to “store” something. That means that once you have put something in a canvas, there is no way to recall  it, or even worst to detect its presence.

This implies that if you want, for instance, to do something when clicking on a “circle” you cannot “bind” an event on that circle, simply because that “circle” doesn’t exist.  So if you need to interact with objects in the scene you need to work around it.

Actually this tutorial will explain how to approach canvas interaction,  so this will result as a shortcut for avoiding the pain and getting only the fun part!

Disclaimer: I know there are lots of html gaming engines as Aves recently acquired by Zinga, CAAT by HyperAndroid, Strikedisplay by Josh Strike or Impact by Dominic Szablewski (this list is far from complete). So if you are not interested in going in depth in canvas technicalities, this is a good point where to stop reading.

If you are interested in going deeper, first of all I suggest you to have a look at what is happening in the canvas world:  canvasdemos is a great source of inspiration. Then have a look to the canvas element reference, just to taste the complexity…

I chose as final destination of this short trip in the canvas world the construction of a simple application,that emulates a lens moving on a picture. In the meantime I’d like to lay the foundations of my own canvas animation(?) library.

imageSumming up I’m going to realize a lens, that will enlarge a part of an underlying background image.

The lens can be moved over the image, to get a detailed view, using drag&drop.

The first step is to have a large image, larger than the screen (e.g.: 4000 x 2000) with full resolution, and use it as canvas background.

In order to have a canvas background you can use at least two different approaches:

1) using CSS background-image. This is an interesting approach as the background is not in the canvas itself, so you do not need to redraw it once you clear the canvas. In my case this solution doesn’t fit as I need to stretch the image, and moreover this feature is not supported by all browsers, actually even canvas is not supported….

2) coping “bits” from an existing image to the canvas. I choose this approach even if this requires to redraw the background after each “clear” action, as this approach is supported by all browser canvas implementations.

First two rows of code: a general style for canvas and the “large” image

image

As the image is “large” everything starts at the “onload” event on the image, that may happen seconds after the page is loaded.

Just a couple of global variables to refer to the image, the stage, and the context.

image

img variable is used only for shortening code and is a jQuery proxy for the <img>. I used jQuery only because I’m used to use it, but it isn’t a requirement.

ctx is the canvas context and is the main entry point for canvas operation (have a look at the complete reference of 2d context); here context is reached via a global variable only for shortening code but the right place (“right” in terms of object modeling) should be in the “stage” object.

stage is the “object model” of our environment, this supplies the lack of state of canvas itself, it holds all the objects of our “world” in an array.

image

First setup fills variables and then initializes the stage:

image

We are creating a canvas with the same aspect ratio of the original image. The Canvas object is our model for html  canvas, it exposes two methods: init and draw.

Init method is responsible for canvas creation, context acquisition, and events binding:

image

Draw method is called every time you need to refresh the scene (really often during interactions) and, in our example, is here only to repaint the background:

image

so, just clear and use the drawImage function to perform a bulk copy from our “large” off-screen  image to the canvas. This is a sort of powerful bitBlit operation that supports crop, translate and resize operations.

Then the Lens object: it is responsible for holding the lens position and magnification. It exposes three methods: draw, moveTo, setMagnification.

The Lens constructor:

image

A little complication here is the fact that we are zooming over an image off-screen while we are moving over the canvas, so we need some scaling factor to calculate the position of the underlying point in the “large” image.

Then lens drawing.

The lens effect is rendered by copying a part of the “large” image, eventually magnified, by cropping the image in a round shape, and applying a radial gradient on an alpha channel.

image

Note once again the usage of drawImage method to copy and enlarge the image and the  clip method for creating a circular clipping region. Then we will apply the radial gradient using color with an alpha channel.

The lens modeled using a js object makes it comfortable to have one or more instances of the same object:

image

image

Here you see two lenses with different sizes and different magnification factors.

Now everything is working but there is no interaction with the user.

Lets try to add drag&drop capability.

I have to cite html5canvastutorials that helped me understand the first steps in managing interactions.

In order to start dragging we have to intercept the mouseDown event on the lens, but as I told you before, canvas do not retain the state of the object you’ve drawn on, so you cannot bind any event on the circle containing the lens: you can only bind events on the whole canvas, so if the user clicks the mouse on the lens you will receive the event on the canvas, not on the circle.

How to determine if there is your lens under the mouse? A simple, mathematical solution is to calculate the distance between the mouse and the lens center (you have all you need, mouse x-y coordinates from the js event, and the lens center from our Lens object), then if distance is  lower than the lens radius the click was inside, out otherwise. image

This approach will work fine mainly for rectangles and circles, but how to manage a complex shape like the one in the picture? (yes, I know that this sucks!)

In this case the “math” approach is definitively more complex.

Hopefully canvas can answer the question “is this point in path?”. The “sole” limitation is that you can ask this question “only while” your are drawing, that in terms of canvas context is only between context.beginPath() and context.closePath() calls; so we have to store somewhere the mouse coordinates, then ask isPointInPath(mouseX, mouseY) and store the result on the Lens object.

Summing up: all events are binded to the canvas object, the stage redrawing is called on mouse move, and the “over” state is recorded on the Lens object. Mousedown, mouseup, and mouseleave only perform simple operations.

image

Stage holds the object currently in drag status.

Note that we move the object that is first dragged on top of the stage.stageObjects list; this will act as a z-order operation, bringing the object to the front.

image

The running example is here, and source code here under MIT licence, so feel free to change and reuse.

All the material referred in this post is in my Licorize booklet for canvas related material: http://licorize.com/projects/rbicchierai/blogmaterial/canvas

I took the photos used as examples in my travels.

Rusty photos

February 3, 2011
tags:

I’ve just published here on Behance a set of close-ups taken last summer in the Faroer Islands.