Sunday, January 10, 2010

Handling Simile Timeline in GWT application.

During a project I had to use Simile Timeline with GXT. I handled mouse wheel zoomin, scrolling, event click handling, dynamically loading events as timeline scrolls etc in GWT-EXT application. I was difficult task and took lot of time to handle these things. Here I am pasting my code for some of the solutions.

How did I change the font size
timeline.css

.timeline-container {
position: relative;
overflow: hidden;
font-size:10;
font-color:red;
}

Added these two lines
font-size:10;
font-color:red;

This style is applied to the div element in which timeline is rendered.


How did I cleare the timeline events to re-render the events on prevously rendered dates.
$wnd.Timeline.timelines[0].getBand(0).getEventSource().clear();

If you want to clear band number 2 the it becomes
$wnd.Timeline.timelines[0].getBand(1).getEventSource().clear();

Handling scroll events

public native static void addScrollListener(int index,
CustomTimeLine timeLine)/*-{
timeLine.getBand(index).addOnScrollListener($wnd.bandScroll);
}-*/;

public static void bandScroll(double startDate, double endDate) {

MessageBox.alert("scroll Event recieved");

}



Handling zoom events

import com.google.gwt.core.client.JavaScriptObject;

public class ZoomStepOptions extends JavaScriptObject {
protected ZoomStepOptions() {
super();
}

public static ZoomStepOptions create() {
return ZoomStepOptionsImpl.create();
}

/**
* required, start date
*
* @param value
*/
public final void setPixelsPerInterval(int value) {
JavaScriptObjectHelper.setAttribute(this, "pixelsPerInterval", value);
}

/**
* required, Unit
*
* @param value
*/
// value =
// Timeline.DateTime.HOUR,Timeline.DateTime.DAY,Timeline.DateTime.MONTH
public final void setUnit(int value) {
JavaScriptObjectHelper.setAttribute(this, "unit", value);
}

}


class ZoomStepOptionsImpl {
public static native ZoomStepOptions create()/*-{
return new Object;
}-*/;
}


import java.util.List;

import com.google.gwt.core.client.JavaScriptObject;

class JavaScriptObjectHelper {

private JavaScriptObjectHelper() {

}

public static native String getAttribute(JavaScriptObject elem, String attr) /*-{
var ret = elem[attr];
return (ret === undefined) ? null : String(ret);
}-*/;

public static native void setAttribute(JavaScriptObject elem, String attr,
String value) /*-{
elem[attr] = value;
}-*/;

public static native JavaScriptObject getAttributeAsJavaScriptObject(
JavaScriptObject elem, String attr) /*-{
var ret = elem[attr];
return (ret === undefined) ? null : ret;
}-*/;

public static native JavaScriptObject[] getAttributeAsJavaScriptObjectArray(
JavaScriptObject elem, String attr) /*-{
var ret = elem[attr];
return (ret === undefined) ? null : ret;
}-*/;

public static native void setAttribute(JavaScriptObject elem, String attr,
JavaScriptObject[] value) /*-{
elem[attr] = value;
}-*/;

public static native void setAttribute(JavaScriptObject elem, String attr,
JavaScriptObject value) /*-{
elem[attr] = value;
}-*/;

public static native void setAttribute(JavaScriptObject elem, String attr,
int value) /*-{
elem[attr] = value;
}-*/;

public static native void setAttribute(JavaScriptObject elem, String attr,
boolean value) /*-{
elem[attr] = value;
}-*/;

public static native void setDateAttribute(JavaScriptObject elem,
String attr, String value) /*-{
var dateValue=new Date(value);
elem[attr] = dateValue;
alert("dateValue="+dateValue);
alert(elem.id + " " + elem.start);
}-*/;

public static native void setAttribute(JavaScriptObject elem, String attr,
float value) /*-{
elem[attr] = value;
}-*/;

public static native int getAttributeAsInt(JavaScriptObject elem,
String attr) /*-{
var ret = elem[attr];
return (ret === undefined) ? null : ret;
}-*/;

public static native float getAttributeAsFloat(JavaScriptObject elem,
String attr) /*-{
var ret = elem[attr];
return (ret === undefined) ? null : ret;
}-*/;

public static int[] getAttributeAsIntArray(JavaScriptObject elem,
String attr) {
int[] rtn = null;
JavaScriptObject hold = getAttributeAsJavaScriptObject(elem, attr);
if (hold != null) {
rtn = new int[getJavaScriptObjectArraySize(hold)];

for (int i = 0; i < rtn.length; i++) {
rtn[i] = getIntValueFromJavaScriptObjectArray(hold, i);
}
}

return rtn;
}

public static native int getJavaScriptObjectArraySize(JavaScriptObject elem) /*-{
if (elem) return elem.length;
return 0;
}-*/;

public static native int getIntValueFromJavaScriptObjectArray(
JavaScriptObject elem, int i) /*-{
return elem[i];
}-*/;

public static native void setAttributeAsIntArray(JavaScriptObject elem,
String attr, int[] value) /*-{
elem[attr] = value;
}-*/;

public static native boolean getAttributeAsBoolean(JavaScriptObject elem,
String attr) /*-{
var ret = elem[attr];
return (ret === undefined) ? null : ret;
}-*/;

/**
* Helper function to create [] array from List.
*
* @param list
*
* @return array of objects
*/
public static JavaScriptObject[] listToArray(List list) {
JavaScriptObject[] array = new JavaScriptObject[list.size()];

for (int i = 0; i < array.length; i++) {
array[i] = (JavaScriptObject) list.get(i);
}

return array;
}

public static JavaScriptObject arrayConvert(Object[] array) {
JavaScriptObject result = newJSArray(array.length);
for (int i = 0; i < array.length; i++) {
arraySet(result, i, array[i]);
}
return result;
}

public static JavaScriptObject arrayConvert(JavaScriptObject[] array) {
JavaScriptObject result = newJSArray(array.length);
for (int i = 0; i < array.length; i++) {
arraySet(result, i, array[i]);
}
return result;
}

private static native JavaScriptObject newJSArray(int length) /*-{
if (length < 0)
{
return new Array();
}
else
{
return new Array(length);
}
}-*/;

public static native int arrayLength(JavaScriptObject array) /*-{
return array.length;
}-*/;

public static native Object arrayGetObject(JavaScriptObject array, int index) /*-{
return array[index];
}-*/;

public static native void arraySet(JavaScriptObject array, int index,
Object value) /*-{
array[index] = value;
}-*/;

public static native void arraySet(JavaScriptObject array, int index,
JavaScriptObject value) /*-{
array[index] = value;
}-*/;
}

38 comments:

  1. is it possible to send a source code (simple will do)... i am not able to run the code.

    ReplyDelete
  2. I do have code, but it is integrated into a big application, it will take some time to extract it and make a sample. Also can you tell me for what purpose you will be using the timeline. In my application I needed only one band, so in some cases I have written the code to handle only band.

    ReplyDelete
  3. thanx for your quick reply. Actually even i am integrating in other application. But as i am not confident, i was trying to make an individual component. I have worked a lot with gwtsimiletimeline, but i am not able to even run my code. Now i am not even sure whether i am doing right or wrong. [even more depressing thing is i dont find any relavant documentation and tutorials in net].

    It would be very kind of you if you could provide me a running project [SIMPLEST WILL DO]. I want to know how to use it.

    and to your question, yes i think i will have single band only.

    eagerly waiting for your response
    Sabbir

    ReplyDelete
  4. Yeah I can feel the pain of implementing the timeline. I also had a hard time getting to it. I am willing to help you. I am going to put the timeline in a separate application and upload it here. It will take atleast two hours. You can see the timeline in action in my application at www.joinusthere.com

    thanks
    Umme Essa

    ReplyDelete
  5. Ok I have done separating the timeline from the application. But how to send it to you?

    ReplyDelete
  6. o that was real kindness of you. you can send me via email @ leo.shabr@gmail.com

    till then i will look at you timeline in action.

    Thanks a lot.

    ReplyDelete
  7. by the way dont get confused with the name, i am the same person (voldemort --> Sabbir manandhar) :)... you can check my profile

    ReplyDelete
  8. i just saw you timeline @ www.joinusthere.com. Its really nice site. and i am sure its really goona help.

    ReplyDelete
  9. Nice post, could you please email me the timeline example at ideanator@gmail.com
    thanks for you help

    ReplyDelete
  10. Hi all

    I got messages about how to disable the event bubble in timeline, here is how you can do it.


    If you see the timeline api you will find a method showBubble which is used internally to display the event bubble. You can overrite it to disable event bubble.

    I am doing so in GWT-EXT application, so I wrote a JSNI method to assing showBubble function to my custom function.


    Following is the JSNI Method.

    public native static void registerCustomeEventClickHandler()/*-{
    $wnd.Timeline.OriginalEventPainter.prototype._showBubble = $wnd.eventClicked;
    }-*/;

    public static native void exposeFunction()/*-{

    $wnd.eventClicked = return "";
    }-*/;



    After you create the timeline call the registerCustomEventClickHandler on timeline object like I have done in following example.

    CustomTimeLineImpl.registerCustomeEventClickHandler();


    Call exposeFunction at the application initialization time.

    This is all you have to do to disable the event bubble.

    If you want to do somethings else rather then just disabling the event bubble you can write the code in $wnd.eventClicked = return ""; instead of return "".

    I hope it is going to help. Let me know if you have any questions.

    ReplyDelete
  11. i get the following error

    $wnd.Timeline.OriginalEventPainter is undefined

    i guess it is when CustomTimeLineImpl.registerCustomeEventClickHandler(); is called

    ReplyDelete
  12. this blog has been really helpful a lot. Why don't you add the matters for loading the data in the timeline dynamically. [In face i am in need of it :) ]

    ReplyDelete
  13. Following is the code to add a event to the timeline dynamically. You need to call your service which will return the event data then you will loop through all records and add one event at a time.

    public class TimeLineEventImpl {
    public static native TimeLineEvent create(String id, double start,
    double end, double latestStart, double earliestEnd, String title,
    String description, boolean instant, String eventImage,
    String eventLink, String eventIcon, String eventID)/*-{
    var eventsToDisplay=new Array();
    var evt= new $wnd.Timeline.DefaultEventSource.Event({
    id:id+"",
    start:new Date(start),
    //end:new Date(end),
    //latestStart:new Date(latestStart),
    //earliestEnd:new Date(earliestEnd),
    //instant: instant,
    isDuration:false,
    text: title,
    description:description,
    //image:eventImage,
    link: eventLink,
    color:'red',
    icon:eventIcon,
    // tapeImage:eventIcon,
    //tapeRepeat:repeat,
    textColor : 'green',
    eventID: eventID
    });
    return evt;
    }-*/;

    }

    The TimelineEvent class

    import com.google.gwt.core.client.JavaScriptObject;

    public class TimeLineEvent extends JavaScriptObject {
    protected TimeLineEvent() {
    super();
    }

    public static TimeLineEvent create(String id, double start, double end,
    double latestStart, double earlistEnd, String title,
    String description, boolean instant, String eventImage,
    String eventLink, String eventIcon, String eventID) {

    // TODO remaining fields
    return TimeLineEventImpl.create(id, start, end, latestStart,
    earlistEnd, title, description, instant, eventImage, eventLink,
    eventIcon, eventID);
    }

    }

    To call the create method use following code.

    ArrayList timeLineEvents = new ArrayList();

    for (TimelineEvents event : events) {
    timeLineEvents.add(TimeLineEvent.create(.....);
    }

    Some of the parameters here are commented out, if you need these parameters add them in the method signature and uncomment the related line.

    ReplyDelete
  14. i did something like this:
    public class TimeLineEventImpl{
    public static native TimeLineEvent create(String id, double start, String title, String description, boolean instant,
    String eventImage, String eventIcon, String eventID)/*-{
    var eventsToDisplay=new Array();
    var evt= new $wnd.Timeline.DefaultEventSource.Event({
    id:id+"",
    start:new Date(start),
    //end:new Date(end),
    //latestStart:new Date(latestStart),
    //earliestEnd:new Date(earliestEnd),
    //instant: instant,
    isDuration:false,
    text: title,
    description:description,
    //image:eventImage,
    //link: eventLink,
    color:'red',
    icon:eventIcon,
    // tapeImage:eventIcon,
    //tapeRepeat:repeat,
    textColor : 'green',
    eventID: eventID
    });
    return evt;
    }-*/;
    }

    The TimelineEvent class
    public class TimeLineEvent extends JavaScriptObject {
    protected TimeLineEvent() {
    super();
    }

    public static TimeLineEvent create(String id, double start, double end, double latestStart, double earlistEnd, String title,
    String description, boolean instant, String eventImage, String eventLink, String eventIcon, String eventID) {

    // TODO remaining fields
    return TimeLineEventImpl.create(id, start, end, latestStart, earlistEnd, title, description, instant, eventImage, eventLink,
    eventIcon, eventID);
    }

    public static TimeLineEvent create(String id, double start, String title, String description, boolean instant, String eventImage,
    String eventIcon, String eventID) {

    return TimeLineEventImpl.create(id, start, title, description, instant, eventImage, eventIcon, eventID);

    }

    public static boolean isPresent(CustomTimeLine timeLine, long eventPk) {
    return TimeLineEventImpl.isPresent(timeLine, eventPk + "");
    }
    }


    and in the entry point class i did:

    public void onModuleLoad() {
    String myData = "data/stonehenge.xml";
    ///simileWidget.load(myData);

    this.myTimeLine = new SimileTimeClass("10%", "100%", new TimelineRenderer());
    //this.myTimeLine.loadData(myData);
    this.myTimeLine.setStyleName("timeLine");
    RootPanel.get().add(this.myTimeLine);
    Date date = new Date();
    TimeLineEvent.create("1", date.getTime(), date.toString(), "", true, "", "", "");

    }


    i have no errors and also i don have any output in the timeline as well

    ReplyDelete
  15. Are you running gwt 2.0 with the simile timeline? Because I cannot even get that to run

    ReplyDelete
  16. I tried running through the get simile timeline "getting started" but keep failing, I'm confused with the package structure, and the whole tutorial all together are there any pointers you can spare?

    ReplyDelete
  17. yea even i started from that site. but it was so depressing.

    Finally this blog has really helped me a lot.

    i hope this may help you (i created for my some personal stuff, code might change daily here): http://code.google.com/p/gwtsimiletimelinesampleproject/

    ReplyDelete
  18. @Shamaila, i worked almost whole day, could not manage the dynamic data loading. Please could you explain a bit more here.

    ReplyDelete
  19. Thanks a lot... will try... post back tomorrow.my findings

    ReplyDelete
  20. Hi Sabbir

    I am sorry to hear that it didn't help you, is there any error when you add the event dynamically? Did you try to debug the application using debugger? Use firbug's command line to manually add event by executing the timeline's add event function.

    I just started to integrate all the code and I was willing to create better documenation for the simile timeline. But unfortunately my son has got a bad fracture due to which I am unable to respond to your quesries on timely basis and unable to continue my work for the getsimile project. I am still trying to help, please just excute the method in firebug and let me know your results.

    Thanks
    Shamaila

    ReplyDelete
  21. Jason I have used the gwtsimiletimeline with gwt1.7+gwt-ext and gwt2.0+smartgwt and it worked fine for both the versions.

    Could you explain what is not working for you? Are you getting any error or you are not getting the timeline at all?

    ReplyDelete
  22. sabbir

    Run your application where you can see the timeline. Open firebug console and paste following function and press run. See the timeline and let me know if you can see the event in timeline. If you get any error please paste here.

    var evt= new $wnd.Timeline.DefaultEventSource.Event({
    id:"",
    start:new Date(start),
    //end:new Date(end),
    //latestStart:new Date(latestStart),
    //earliestEnd:new Date(earliestEnd),
    //instant: instant,
    isDuration:false,
    text: "test,
    description:"description",
    //image:eventImage,
    //link: eventLink,
    color:'red',
    icon:"ValidIcon line here",
    // tapeImage:eventIcon,
    //tapeRepeat:repeat,
    textColor : 'green',
    eventID: 1
    });

    ReplyDelete
  23. i pasted in console

    var evt= new Timeline.DefaultEventSource.Event({
    //id:"",
    start:new Date(1285049089917),
    isDuration:false,
    text: "title",
    description:"description",
    color: 'red',
    eventID: 1
    });

    and run

    there is no error but no event in the timeline as well

    ReplyDelete
  24. do you have any idea on error:

    3 [ERROR] [ap] Uncaught exception escaped
    com.google.gwt.core.client.JavaScriptException: (TypeError): v1.getTime is not a function
    fileName: http://127.0.0.1:8888/js/timeline_2.3.0/timeline_js/bundle.js
    lineNumber: 252

    ReplyDelete
  25. I did it :) finally.

    But not by the way you told. You had implemented the funtion loadXmlText(). I used it and i could load the date in run time.

    ReplyDelete
  26. Shamaila,

    Ah that might be a problem I was trying to use GWT2.0 with GWTEXT not smart gwt. I'll try that and get back to you

    Jason

    ReplyDelete
  27. hi Jason

    I have used the timeline api with both GWTExt and smartGWT, it should work with both.

    ReplyDelete
  28. Hey Shamaila,

    I Got everything up and running and I am working on customizing the timeline and will let you know how everything goes, thanks for all your help

    Jason

    ReplyDelete
  29. ANother problem

    I am having problems when it comes to setting a bottom band as highlighting. I am not sure what is wrong. It seems that highlighting and setting the bottom band as a true does not work. I am fine with syncing the band with another band.

    I was wondering if there was a different way that you are supposed to be doing the java code to set a boolean for the javascript.

    Jason

    ReplyDelete
  30. Hey Shamaila,

    So I have the timeline up and running but it doesn't appear to be rendering everything correctly.

    1. My duration events, the tapes are not showing up correctly.
    2. My non-duration events, the onClick does not show the dialog.
    3. The highlighters are not working.

    Could this be some configuration problems that I have?

    Jason

    ReplyDelete
  31. Hi Jason

    How are you fetching your data? If JSON or XML please send me sample of your XML/JSON. Taps not showing may be an XML problem. Highlighters may be some configuration issue.

    ReplyDelete
  32. Hey thanks for the quick reply.

    My XML file simply the event with a start time and end time and isDuration="true". I believe to get a duration event that is all you need a start date and an end date and setDuration="true". I have seen a different parameter "instant" could this be the problem that I am having.

    As for the configuration problem. I have been hearing that from multiple sources and am wondering what is there to configure? What could be causing the timeline to not paint the highlighters. Also why is it that my duration events are clickable versus my point events are not clickable (I am noticing this because of the dialog that appears on click of an event)



    Thanks Jason

    ReplyDelete
  33. Hello, your work @ www.joinusthere.com is great!, how do you manage to relate timeline events with google map? I am working on something similar and I will appreciate your help :)

    Thanks

    ReplyDelete
  34. hi Claudia

    The events you see on the timeline are stored in the db, the table contains startTime, endTime and latitude longitude of the venue. We update the timeline and map whenever they are scrolled and at some other events too. Like at the initilization time the application finds the current location of the map and current time slot on the timeline. Then the application queries the backend, which returns the events within that timeframe and location. This is how we are doing it.

    Let me know if you need further clarifications.

    Thanks

    ReplyDelete
  35. Thank you for the reply.

    I will try to make it work taking this into account. I will let you know my results.

    Bests,

    ReplyDelete
  36. Nice post, could you please email me the timeline example at ravi123_kumar@yahoo.com
    thanks for you help

    ReplyDelete