6V8 - Production of my Mind

Home page > For your Computer > Objective Degradation > ObjectiveDegradation and Microformats

ObjectiveDegradation and Microformats

 
ObjectiveDegradation is a small javascript API that relies on the xhtml embedded semantics to build javascript objects and perform a separation between the layout and the behaviour. This article will walk you through a small example based on the hCalendar microformat.
hCalendar example. | 24 January 2006, by Mortimer

1 The source code

First, the finished example is available here, look at the html source code and that the javascript file attached.

2 hCalendar Microformat

hCalendar is a simple microformat to describe events in XHTML following the same format as ICS calendars. See this page for a good introduction.

In summary, it maps the ICS format for an event to a human readable XHTML. So it will look something like that:

ICS


BEGIN:VEVENT
URL:http://www.web2con.com/
DTSTART:20051005
DTEND:20051008
SUMMARY:Web 2.0 Conference
LOCATION:Argent Hotel\, San Francisco\, CA
END:VEVENT

XHTML


<span class="vevent">
 <a class="url" href="http://www.web2con.com/">
  <span class="summary">Web 2.0 Conference</span>:
  <abbr class="dtstart" title="2005-10-05">October 5</abbr>-
  <abbr class="dtend" title="2005-10-08">7</abbr>,
 at the <span class="location">Argent Hotel, San Francisco, CA</span>
 </a>
</span>
 

The content of the two code snippets is the same. The sole difference is that it is simpler to display, in human readable form, the XHTML than the ICS format.

The important thing to note is that the semantic of each element is kept between the two formats:

there is a vevent block with different properties

2.1 Javascript Object Representation

This semantic is interesting as it gives a fast correspondence with computer code. For example, here is some javascript object code that could represent a vevent:


Vevent.prototype = {
    dtstart: '',
    dtend: '',
    url: '',
    location: '',
    summary: ''
}
 

3 Our Example

In our example, we will try to make a small form to create events and add them to two possible calendars: work or home.

Note: This example covers only the javascript part of the code and I am not going to do the server-side code to manage database insertion etc... So when it degrades, it is not fully working, but this is due to server-side issues and not the javascript.

3.1 The Form

First of all, we need a form to enter the event data. This will allow us to have post submission when the javascript is not activated.

The form is supposed to insert vevent in our (hypothetical database), so we are going to follow the vevent microformat here too:


<form class="Vevent" action="hcal.html" method="post">

<label for="dtstart">Start</label>
<input type="text" name="dtstart" class="dtstart" id="dtstart"/>

<label for="dtend">End</label>
<input type="text" name="dtend" class="dtend" id="dtend"/>

<label for="url">URL</label>
<input type="text" name="url" class="url" id="url"/>

<label for="summary">Summary</label>
<textarea name="summary" class="summary" id="summary"></textarea>

<label for="location">location</label>
<input type="text" name="location" class="location" id="location"/>

<input type="submit" class="addMe" name="add" value="Add Event"/>

</form>
 

Note that all the element we want to collect have a specified class representing the property of the vevent it represents (for the fields) or action on this vevent it triggers.

3.2 Javascript Behaviour

So, let see some basic behaviour we could want this form to have:


Vevent.prototype = {
    dtstart: '',
    dtend: '',
    url: '',
    location: '',
    summary: '',

    addMe: function() {
                alert('New Event Added');
    }
 

So we stick to the OO design: an object with some properties and a method to do something with it.

You will have noticed how I kept the object naming similar to the class attribute in the XHTML code. This is what ObjectiveDegradation will use to make our life simple.

So lets notify ObjectiveDegradation that this object has a meaning for us:


ObjectiveDegradation.register({
        'Vevent': Vevent
});
 

This simple command at the end of the file create an ObjectiveDegradation object that will take care of mapping Vevent javascript objects to XHTML block with the class Vevent.

This is it for now, it works as is. Now, if you go and click on the button «Add Event». ObjectiveDegradation will take care of everything for you, it will:

  1. instantiate a Vevent object,
  2. set all its properties to the value of the corresponding field (by their class attribute) in the form,
  3. call the addMe method on the new instance.

So you should see a simple alert dialogue that says «New Event Added».

Great! this was simple, isn’t it?

3.3 Calendar Object

So now, we would want to do something more interesting with this: adding events to a calendar.

In a real-life application, you would make a call to store the event on the server, here we are just going to display the event on the page.

3.3.1 Layout

First of all, we need to represent the two calendars on the web page:


...
</form>

<div class="Calendar" id="home">
<h1 class="title">Home Calendar</h1>
<h4 class="Counter event_count"><strong class="value">0</strong> events</h4>
</div>

<div class="Calendar" id="work">
<h1 class="title">Work Calendar</h1>
<h4 class="Counter event_count"><strong class="value">0</strong> events</h4>
</div>
 

Here again, you can notice that we semantically describe the calendar with the class attributes. We have a Calendar with two properties:

  1. a title,
  2. a counter of the number of events present, this one also has a property:
    • a value containing the number of events.

3.3.2 Objects

This easily maps to OO designed code as we made sure to have a meaningful semantic:


var Calendar = Class.create();

Calendar.prototype = {
    title:'',
    event_count:'',
    vevent_container: Array(),
    add: function(vevent) {
        var container = document.createElement('div');
        container.appendChild(vevent.draw());
        this._element.appendChild(container);
        this.vevent_container[vevent.id] = container;
        this.event_count.increment();
    },

    remove: function(vevent) {
        container = this.vevent_container[vevent.id];
        if(container) {
            this._element.removeChild(container);
            this.event_count.decrement();
        }
    }
}

var Counter = Class.create();

Counter.prototype = {
    value: 0,
    increment: function() {
        this.value++;
        this.updateValue();
    },
    decrement: function() {
        this.value--;
        this.updateValue();
    },
    updateValue: function() {
        this._element.firstChild.innerHTML = this.value;
    }
}
 

3.3.3 Interesting?

This code shows a bit more of the power of ObjectiveDegradation. We use two objects, encapsulating (in the layout) a Counter for each Calendar. ObjectiveDegradation will automatically instantiate Counter objects and point to them with the event_count property of the Calendar object.

A second interesting thing to remark is the use of this._element. ObjectiveDegradation will automatically set the _element property of each object it instantiates to the DOM element that it is mapped on.

3.4 Co-referencing

So, we have a form that submits events and calendars to show them. We now need to glue everything together. The addMe method of the event should be able to call the add method of one of the two calendar.

Here ObjectiveDegradation also have a nice feature. It will consider every inner document anchor (<a href="#anchor" class="myproperty">) as a reference to another object in the document.

What we need to do is then to put such reference in our Vevent object and modify the add and delete methods to perform according to them:


     //  ...
    home_calendar: null,
    for_home: '',
    work_calendar: null,
    for_work: '',

    addMe: function() {
        if(this.for_work) {
            this.work_calendar.add(this);
        } else {
            this.home_calendar.add(this);
        }       
    },

    deleteMe: function() {
        if(this.work_calendar) {
            this.work_calendar.remove(this);
        } else {
            this.home_calendar.remove(this);
        }       
    },
 

home_calendar and work_calendar will therefore be reference to the calendar in which to add this event [1].

Next, we have to reflect this new properties in our form:


....
<input type="radio" name="for" value="home" id="for_home" class="for_home"/>
<label for="for_home">add
<a class="home_calendar" href="#home">to the home calendar</a></label>

<input type="radio" name="for" value="work" id="for_work" class="for_work"/>
<label for="for_work">add
<a class="work_calendar" href="#work">to the work calendar</a></label>
....
 

This code add a radio button to select the right calendar and an href to the calendar element in the page for each calendar. This href could seem obsolete, but remember that for accessibility reason, it is always good to explicitly mention the context of an action.

ObjectiveDegradation will do all the rest for us. We just have to register all of our object and here we go:



ObjectiveDegradation.register({
        'Vevent': Vevent,
        'Calendar': Calendar,
        'Counter': Counter
});

 

3.5 Object transfer

This is a tricky part, beware!

To support the Calendar methods, we have added some methods to the Vevent objects:

   
deleteMe: function() {
        if(this.work_calendar) {
            this.work_calendar.remove(this);
        } else {
            this.home_calendar.remove(this);
        }       
    },

draw: function() {
        var node = document.createElement('span');
        ObjectiveDegradation.copyToNode(this,node);
        node.className = 'Vevent';
        node.id = "vevent"+this.id;
       
        var url = document.createElement('a');
        url.className = 'url';
        url.href=this.url;
        node.appendChild(url);
       
        url.innerHTML =
        '<span class="summary">'+this.summary+'</span>:'+
        '<abbr class="dtstart" title="'+this.dtstart+'">'+this.dtstart+'</abbr>-'+
        '<abbr class="dtend" title="'+this.dtend+'">'+this.dtend+'</abbr>,'+
        'at the <span class="location">'+this.location+'</span>';
       
        var deleteMe = document.createElement('a');
        deleteMe.className = 'deleteMe';
        deleteMe.href='delete'+this.id;
        deleteMe.innerHTML = 'Delete';
        node.appendChild(deleteMe);
        return node;
}
 

The draw method we are adding creates a new node in the document to represent the Vevent object. You should remember that this object has been created as a representation of the form block, now that we are putting this object in the calendar, we need a new XHTML representation of the object.

By creating this new representation, ObjectiveDegradation will think we created a new independent instance of the Vevent object. This is really important to understand as it means that the new block will be represented by another instance of the Vevent Class that will not pertain the values initiated in the first object if we do not explicitly specify that the two DOM objects are the representation of the same object.

It is therefore important for us to specify to ObjectiveDegradation that this element represents our initial instance. Otherwise, we will loose the information that we do not duplicate in the html (here, for example, we do not mention any more the calendar in which we are inserting the event). Hopefully, pertaining an object between different DOM representation is simple, you just have to set the element _obObject property to point on the instance you are transferring:


node._odObject = this;
 

In fact, as for the _element property, ObjectiveDegradation automatically fill (or use, if already present) the _obObject property to link a javascript instance with its representation in the DOM.

3.5.4 Persistance Issue

If you have tried what I have just said before, you will see a strange behaviour: the events in our calendar will not be deleted correctly.

In deed, everytime you create a new event with the form, you are not creating a new Vevent instance but just modifying the old one associated with the form. As this object is referenced from every event block in our calendars, the modification is also made to every instance attached to the Vevent blocks in the calendar.

This is a feature of the persistance, but is an issue in our application. In deed, we would like to copy a snapshot of the instance attached to the form when we create a new representation of the event to put in the calendar. ObjectiveDegradation is your friend here again. It provides a simple static method that you can use to make a copy of an instance and transfer it a new DOM element:


ObjectiveDegradation.copyToNode(this,node);
 

4 Everything Together

The full example is here to see (the source contains event more goodiesclin d'oeuil).

If you have any question, use the forum here or on the introduction page.

This example is continued by the tutorial on Polymorphism and Inheritance and will be extended to use ajax with ObjectiveDegradation in the upcoming tutorial on patterns and multiple inheritance.

Date of online publication: 24 January 2006
last-update: 10 November 2006
Forum messages 0
visits:
2437

Creative Commons Attribution NonCommercial ShareAlike 2.5  License

notes

[1] Note: this is probably not the best OO design, but is adopted for simplicity of the example.

 

Reply to this article

 

The most read articles

 
©
Pierre Andrews
York, uk
| Site Map | Site created with SPIP 1.9.2c [10268] | RSS | template by IZO, Mortimer. | clicky stats
an documented point Raris is
an documented point Raris is
an documented point Raris is
an documented point Raris is
an documented point Raris is