Delicious Tag Graph

Code Camp, Sun Lively Kernel Application
0035087 Petri Heinilä
2008-10-24 Lappeenranta

Application

Delicious Tag Graph allow user to visualize his bookmarks on http://delicious.com . Delicious.com provides web based bookmarking service where bookmarks can be grouped and managed by tags. Tag is a simple name presenting some field. Bookmark can have multiple tags, so they can intersect each other field. With this information it is possible form relationships between tags. Delicious Tag Graph presents tags as graph nodes and relationships as edges. From edge user and then see a list of bookmarks involving to the relationship, and of course navigate along the bookmark link.

Sun Lively Kernel provides a way to dynamically modify browser side SVG document object model (DOM), thus implementing very graphical oriented programming environment. Graph presentations benefit graphical implementation background the lively kernel is providing.

Delicious Tag Graph in Safari web browser Delicious Tag Graph in Firefox wen browser, reactivity was not so good than in safari.

Design

Initial design consists of graph management objects. Classes are inherited from lively kernel graphical primitive classes. Morph is a graphical entity with shape and spatial space allocation. ClipMorph constrains sub-morph space allocation to given area. TagGraph manages the graph view. It contains the things and relationships between them. Thing presents the tag. Thing is able to be moved by the use so that relationships are following the movement. Relationship presents the edges in the graph.

Implementation

Implementation status:

  • Presentation: Graph Creation and Management (done)
  • Communication: XMLHttpRequest to delicious.com (not done)
  • Logic: Bookmark information parsing and calculation (not done)

On implementation the biggest problem was lack of reference manual. The lively kernel source code work as primary functionality discovery source, but it was very slow to find out relevant things out of it. So time gone to find out functionality and no communication or logic implementation were done.

Running the application

  • Unzip delicious_tag_graph-<REV>-<DATE>.zip
  • Open delicious_tag_graph-<REV>-<DATE>/aa_test.xhtml with Safari web browser

References

Code Camp

SVG

Sun Lively Kernel

Code

/* Delicious Tag Graph

Copyright (c) 2008 Petri Heinil�.

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", IF THE SOFTWARE DRINKS YOUR BEER,
THEN IT IS WAY TOO BAD, WE COULD NOT CARE LESS.
*/

/****************************************************************************
 * Create application with window.
 */
var create_delicious_tag_graph = function() {
  var widget;
  var window; 
  widget = new TestMorph(new Rectangle(20,50,500,500));
  window = new WindowMorph(widget,"Delicious Tag Graph");
  return window;
}

/****************************************************************************
 * Thing
 */
Morph.subclass("Thing",{
  initialize: function($super,ctx,rect,text){
		this.text = new TextMorph(new Rectangle(0,0,0,0),text);
    this.text.beLabel();
		//this.magic_thing = true;
		this.relationships = [];
		var nrect = new Rectangle(
		  rect.x,rect.y,
		  this.text.bounds().width,
			this.text.bounds().height
		); 
    $super(nrect,'rect');
    this.openForDragAndDrop = true;
    this.relayMouseEvents(this,{ 
		  onMouseDown: "thingMouseDown", 
			onMouseUp: "thingMouseUp", 
			onMouseMove: "thingMouseMove"
			});
		this.addMorph(this.text);
		this.ctx = ctx;
		return this;
  },
	center: function() {
		return this.bounds().center();
	},
	thingMouseDown: function(evt) {
		// console.log("Thing.thingMouseDown " + evt);
		return true;
	},
  thingMouseUp: function(evt) {
    // console.log("Thing.thingMouseUp");
		return true;
  },
  thingMouseMove: function(evt) {
    var cb = this.bounds(); // current this morph bounds
    var cwp = this.owner.localize(evt.mousePoint);
    var nb = new Rectangle(cwp.x, cwp.y,cb.width,cb.height);
    this.setBounds(nb);
		this.update();
		return true;
  },
	update: function() {
		this.ctx.update();
	},
	
}); // Thing

/****************************************************************************
 * Relationship 
 */
Morph.subclass("Relationship",{
	initialize: function($super,ctx,aThing,bThing,text) {
		$super(pt(0,0).asRectangle(),'rect');
		this.ctx = ctx;
		this.aThing = aThing;
		this.bThing = bThing; 
    this.applyStyle({
			fill: null,
			borderWidth: 1,
			borderColor: Color.black,
			});
		console.log("LINE " + this.aThing.center() + " " + this.bThing.center());
		this.shape = new PolylineShape([this.aThing.center(),this.bThing.center()],
		 1, Color.black);
    this.morph = new Morph(pt(0,0).asRectangle(), 'rect');
    this.morph.applyStyle({
        fill: null,
        borderWidth: 1,
        borderColor: Color.black
    });
    this.morph.setShape(this.shape);
		this.update();
	},
	update: function() {
		// console.log("Relationship.update");
		this.shape.setVertices([this.aThing.center(),this.bThing.center()]);
		this.morph.changed();		
    this.morph.layoutChanged();
    this.morph.setShape(this.shape);		
	},
	get_morph: function() {
		return this.morph;
	},
})

/****************************************************************************
 * Tag Graph
 */
ClipMorph.subclass("TestMorph", {    
	initialize: function($super, rect) {
		$super(rect, "rect");
		console.log("TestMorph initialize"); 
    this.thing_num = 0;
		this.relationship_num = 0;
		this.things = [];
		this.items = [];
		this.relationships = [];
		this.openForDragAndDrop = true;
    this.layout();
		//this.setFill(Color.veryLightGray);
    return this;
    },

  update: function(evt) {
    var i;
    for(i = 0; i < this.relationships.length; i += 1) {
      var r = this.relationships[i];
      r.update();
    }   
  },

  create_Thing: function() {
    var name = "thing-" + this.thing_num;
    this.thing_num += 1;
    var cr = this.bounds();
    var r = new Rectangle(0,0,100,60);
    r.x = (cr.width - r.width) / 2.0 + 
      Math.random() * (cr.width - r.width) - ((cr.width - r.width) / 2);
    r.y = (cr.height - r.height) / 2.0 + 
      Math.random() * (cr.height - r.height) - ((cr.width - r.width) / 2);
    var t = new Thing(this,r,name);
    this.addMorph(t);
    this.things.push(t);
    this.items.push(t);
    return t;
  },
  
  create_Relationship: function(aThing,bThing) {
    var name = "relationship-" + this.relationship_num;
    this.relationship_num += 1;
    var r = new Relationship(this,aThing,bThing,name);
    r.update();
    // alert(r);
    this.addMorphBack(r.morph);
    this.relationships.push(r);
    this.items.push(r);
    return r;
  },   

		
	layout: function() {
		this.test_layout3();
	},
	
	test_layout1: function() {
    var a = this.create_Thing();
    var b = this.create_Thing();
    var c = this.create_Thing();
    var d = this.create_Thing();
    var e = this.create_Thing();
    var f = this.create_Thing();
    this.create_Relationship(a,b);
    this.create_Relationship(a,c);
    this.create_Relationship(a,d);
    this.create_Relationship(a,e);
    this.create_Relationship(b,c);
    this.create_Relationship(c,d);		
    this.create_Relationship(f,d);    
    this.create_Relationship(f,a);    				
	},
	
	test_layout2: function() {
		var i;
		for(i = 0;i < 50;i += 1) {
      var a = this.create_Thing();		
		}		
	},

  test_layout3: function() {
    var i;
		var j;
    for(i = 0;i < 7;i += 1) {
      var a = this.create_Thing();    
    }   
		for(i=0;i < this.things.length;i+=1) {
			var it = this.things[i];
      for(j=0;j < this.things.length;j+=1) {
        var jt = this.things[j];
        var r = this.create_Relationship(it,jt);     
      };			
		};		
  },
			
}); // TestMorph