Overview of design and implementation of behaviors for Diem 5.4 DEV and road map for 5.5 release

Posted: November 16, 2011 in Programming

It sounded as a good idea to separate all the effects and JavaScript visual improvements in completely separate piece of code that would be attached to the widget. In practice – it shown up even better! Truly, I was amazed how this concept fits in Diem. Almost as someone has builded up this system in Diem, and then remove it in order to be discovered again. Every jQuery fancy plugin that I could remember – I have implemented in matter of 10-20 hours, and I am using that in three project already. The reusability value is almost higher as widgets.

Nevertheless – some problems appeared and some new requirements as well, so refactoring is must and in that matter, redesign of the concept should occur as well. Since I have initiate this feature and extensively worked on development of the behaviors, here are my observation of the current implementation and future road map.

1. Behaviors are applied to the widgets – they should be applied to the zones as well.

Why? I have implemented several widgets that cycles, scrolls, shuffle, mix various DOM elements (images, divs, paragraphs, etc…). Mixed content, right? TinyMce widget is great for that – but for end user, widgets, as separated, independed components are easier to use and to teach end user how to use them. So, per example, if user wants to cycle or scroll mixed content – he can add to the zone the widgets and attach to that zone a behavior that will cycle/scroll widgets.

Other usage – that I practice now, is to attach behavior to the widget that will find container zone and then do the cycling. It is wrong because:

  • If I want to cycle 20 widgets – I can/should attach a cycle behavior to one of those widgets. Which one? Random? Ok, no problem – it will find a container zone and do the magic. Two days latter, I will reorder the positions of the widget – first will go to the middle, 19th becomes 2nd and so on… 20 days latter…. On which widget I have attached the behavior that cycles all the widgets in the zone? I forgot! Go one by one and find it in order to change the settings… So – it is semantically stupid!
  • Of course – for end user (who is usually dumb 🙂 ) using this concept – attach the cycle behavior to the zone in order to cycle all the widgets is easily to understand, rather to: attach to any widget. It is more natural.

2. On/Off behaviors in admin mode of the page.

This is a must! I figure out with jCarouselLight (it is a great, simple jQuery plugin for scrolling widgets/content). I had set automatic scrolling by accident to be 200ms! Hell – I had to chase the widget who had attached behavior in order to change the setting! (this could never happened if the behavior was attached to the zone). So, “on” means run the behaviors, “off” do not run/remove behaviors.
One problem occurred in that cycling I could not rearrange the order of widgets as well – they are cycling :).
So, each behaviors should have 4 following metthods:
  • $(‘zone or widget’).myBehavior(‘init’, {}); {} <-configuration of the behavior as JSON object
  • $(‘zone or widget’).myBehavior(‘start’);
  • $(‘zone or widget’).myBehavior(‘stop’);
  • $(‘zone or widget’).myBehavior(‘destroy’);

3. Sequence of calling init/start/stop/destroy

This is edge use case scenario, and this is if you are attaching more than one behavior to the widget/zone. Per example, one behavior modifies the HTML content (per example <div> to <div style=”width:100px; height: 100px; overflow:hidden”> and second do some magic (per example cycles those divs). This will not be used in 90% of cases, but if sequence is important -> without of this, whoever needs this functionality is in trouble if cycle() fires first, then resize().
So, this is order, per example: 1,4,5,8
init() -> 1.init(), 4.init(), 5.init(), 8.init()
start() -> 1.start(), 4.start(), 5.start(), 8.start()
stop() -> 8.stop(), 5.stop(), 4.stop(), 1.stop()
destroy() -> 8.destroy(), 5.destroy(), 4.destroy(), 1.destroy()
or: init and start=> FIFO, stop and destroy => LIFO

4. Behaviors should be able to change the state of the others zones/widgets

This is quite hard to explain, but I see widgets as small, unique, simple piece of code. Complex page is created from simple widgets -> they are building components. We should avoid to create complex widgets. This is example: http://coffeescripter.com/code/ad-gallery/ (there are better scripts 🙂 but ok, give a credit to an author). So, it can be created as complex widget or combination of simple widgets with behaviors?
Lets take second approach – complex is builded from simple. Main, big image is image widget (widget_1). Small, thumbs are same, image widgets (widgets_2 to widgets_n). I want to change the SRC of the image in widget_1 when I click on widget_n.
I have done this with behaviors, like this: widget_1 has a css class .panel and behavior seeks the image with that class and changes the SRC when widget_n is clicked.
Why do I attach the .panel class? Because, in admin mode of the page, each zone/widget has ID, like <div class=”dm_widget” id=”dm_widget_1″> or <div class=”dm_zone” id=”dm_zone_1″>, but when you are logged off,  you get: <div class=”dm_widget”> or <div class=”dm_zone”> -> so I have to attach it because in prod view state -> IDs are not rendered.
This inflicts the usability. It would be convenient to have zone/widget ID in logoff state as well. Like this:
<div class=”dm_widget dm_widget_1″> or <div class=”dm_zone dm_zone_1″> (I hate IDs!). Why?
  • Easier coding of behaviors that changes the state of other zones/widgets on which behaviors are not applied – you can relay on IDs
  • User friendly for dumb ass end user – you do not have to explain to him “you have to enter some class first…” and he say “why? what is a class?” Instead, you can create drag&drop interface for behavior setting, like drag widget in this input text box on which you want bigger picture to be displayed
So – this inflicts corrections in rendering widgets/zone, but it is easy to implement…

5. Cut, Copy, Paste support

We should be able to copy/cut/paste behaviors same as widgets.

6. behaviorsManager JavaScript

It will be a entry point JavaScript class singleton instance for all behaviors that will deal with behaviors. How?
Here is the snippet: <div class=”behaviorable”> – this is how the behavior manager knows on which element behavior is attached. So, it can be attached to zone <div class=”behaviorable dm_zone”> or widget <div class=”behaviorable dm_widget”>. And thats not all! You already figure out, behaviors can be attached to anything, any HTML DOM object! ANY!!!! Very powerful for further development of behaviors!
How to read which behavior is attached with which settings? The answer is JSON and metadata(). Why? It is compliant with both HTML 5 and HTML 4. So here is the snippet:
<div class=”behaviorable {random: random, {random: random}, behaviors: []}” > – I have entered the “random” just to show that it has to be a space to add some other JSON if it is required for whatever reason. Lets continue…
<div class=”behaviorable behaviors: [tipsy: [{seq: 1, settings: settings}], fade: [{seq: 2, settings: settings}, {seq: 3, settings: settings}] ]}” > (seq: 1 => it is a sequence order from bullet point 3)
Notice the arrays in settings in each behavior, tipsy, fade? Fade is attached twice? Why? I have created this use case for image hover effect, for image I have attached fade twice, on mouse over fade to  50% of opacity, on mouse out fade to 100% opacity.
Fade should be for mouse over, mouse out? Wrong!!!!! From simple things we are creating complex!!!! Attach it twice with different settings and make it complex! MAJOR RULE! So fade behavior has two settings, event and opacity – and by combining them you are creating the complex things.
So, behaviorManager finds a DOM elements with class “behaviorable” reads the settings, set the sequence and after widgets are initialized -> fires up behaviors. Thats it.

7. How to build it?

It is a plugin. The convention is dmNameOfTheBehaviorBehaviorPlugin. Lets create one: My
dmMyBehaviorPlugin
 – config
      – dm
         – behaviors.yml
– lib
– web
– (it is a common plugin)
behaviors.yml is major configuration, so lets see its content:
dmBehaviors: # IMPORTANT -> HAS TO EXIST LIKE THIS
  my: # UNIQUE NAME, UNIQUE ID OF THE BEHAVIOR
    section: Utils # ALL BEHAVIORS ARE DIVIDED IN SECTIONS, LIKE WIDGETS. HUMAN READABLE
    name: Something normal # HUMAN READABLE NAME, "MY" IS JUST STUPID :)
    #icon: /dmImageOverlayBehaviorPlugin/images/icon.png # OPTIONAL, ICON FILE, CAN BE OMITTED
    form: dmMyBehaviorForm # FORM FOR ENTERING THE SETTINGS
    view: dmMyBehaviorView # VIEW, ACTUALLY FILTERING AND MODIFYING SETTINGS FROM FORM AND LOADING NECESSARY JAVASCRIPTS AND STYLESHEETS
Two important files, form and view:
FORM:
class dmMyBehaviorForm extends dmBehaviorForm {

    public function configure() {
        parent::configure();

        // Add sfWidgets and sfValidators for form
    }

    public function render($attributes = array()) {
        return 'rendered form HTML code'
    }

    public function getStylesheets() {
        return array_merge(
            parent::getStylesheets(), array() // my stylesheets, if necessary
        );
    }
    public function getJavaScripts() {
        return array_merge(
            parent::getJavaScripts(), array() // my javascripts, if necessary
        );
    }

}
VIEW:
 */
class dmMyBehaviorView extends dmBehaviorView {

    protected function filterSettings($settings) {
        $settings = parent::filterSettings($settings);
        // DO MODIFICATIONS AND FILTERING
        return $settings;
    }

    public function getJavascripts() {
        return array_merge(
            parent::getJavascripts(),
            array(
                // MY JAVA SCRIPTS IN ORDER FOR BEHAVIOR TO WORK, launch.js, per example :)
            )
        );
    }

    public function getStylesheets() {
        return array_merge(
            parent::getStylesheets(),
            array(
                // MY CSS IF BEHAVIOR REQUIRES THEM
            )
        );
    }
}

Ending thoughts

This is the proposal for the implementation of the behaviors (refactoring). I have tried the concept in Diem without most of this feature that I have stated implemented – and it is so powerful, it is in production for my 3 websites.
But major concept is the following: the page in Diem is created from simple things. If you can support this concept, then, there are a good foundations for the behaviors to be implemented and heavily used in Diem.
Thank you for reading.

Leave a comment