Timeline helper

5 : Code

By Ronny Vindenes (ronnyvv)
Easy to use helper for the MIT Simile Web Widgets component Timeline. Timeline allows web site creators to embed interactive timelines into their sites. It requires only that visitors have Javascript enabled. It's often referred to as "Google Maps" for time.

Helper Class:

<?php 
/**
 * Helper for using SIMILE Timeline
 *
 * Examples:
 * 
 *  Timeline with single band, with data from /controller/json/$data['Timeline']['id']
 * 
 * <?php
 *    echo $timeline->create(array('style'=>'width:200px; height:100px;'));
 *    $timeline->band();
 *    $timeline->setDataSource(array('action'=>'json', $data['Timeline']['id']));
 *    echo $timeline->end();
 * ?>
 * 
 * Timeline with two bands showing the same data using two different timescales:
 * 
 * <?php
 *    echo $timeline->create(array('style'=>'width:200px; height:100px;'));
 *  $timeline->band(array('width'=>'"80%"','intervalUnit'=>'Timeline.DateTime.MONTH','intervalPixels'=> 100));
 *    $timeline->band(array('width'=>'"20%"','intervalUnit'=>'Timeline.DateTime.YEAR','intervalPixels'=> 200));    
 *    $timeline->setDataSource(array('action'=>'json', $data['Timeline']['id']));
 *    echo $timeline->end();
 * ?>
 * 
 * Timeline with two bands showing the same data using two different timescales and different styles:
 * 
 * <?php
 *    echo $timeline->create(array('style'=>'width:200px; height:100px;'));
 *  $timeline->band(array('width'=>'"80%"','intervalUnit'=>'Timeline.DateTime.MONTH','intervalPixels'=> 100));
 *    $timeline->band(array('width'=>'"20%"','intervalUnit'=>'Timeline.DateTime.YEAR','intervalPixels'=> 200, 'overview'=>'true'));    
 *    $timeline->setDataSource(array('action'=>'json', $data['Timeline']['id']));
 *    echo $timeline->end();
 * ?>
 * 
 * 
 * @author Alexander Morland
 * @author Ronny Vindenes
 * @category Cake Helper
 * @license MIT
 * @version 1.0
 * 
 */
class TimelineHelper extends AppHelper {
    
    public 
$helpers = array('Html''Time''Javascript');
    
    private 
$bandGroups = array();
    private 
$timeSyncs = array();
    private 
$bands = array();
    private 
$divId;
    private 
$bandCount 0;
    
    private 
$bandInfoDefaults = array(
            
'width' => '"100%"'
            
'intervalUnit' => 'Timeline.DateTime.DAY'
            
'intervalPixels' => 100);
    
/**
     * Define a new band on the timeline
     *
     * @param array $options options for Timeline.createBandInfo()
     * @param string $bandGroup named group of bands to share datasource with
     * @param string $timeSync named group of bands to syncronize with
     */
    
public function band($options = array(), $bandGroup 'main'$timeSync 'main') {
        
$band am(array('eventSource' => 'eventSource_' $bandGroup), $this->bandInfoDefaults$options);
        
$this->bands[] = $band;
        
$this->bandGroups[$bandGroup][] = $this->bandCount;
        
$this->timeSyncs[$timeSync][] = $this->bandCount++;
    }
    
    
/**
     * Initilizes timeline and renders the div that will contain the timeline
     *
     * @param array $attributes html attributes for the div containing the timeline
     * @param string $id
     * @param boolean $createDiv
     * 
     * @return string DIV element if created
     */
    
public function create($attributes = array(), $id 'timeline'$createDiv true) {
        
$this->Javascript->link('http://static.simile.mit.edu/timeline/api-dev/timeline-api.js?bundle=true'false);
        
$this->divId $id;
        
        
$html '';
        
        if (
$createDiv) {
            
$html .= $this->Html->div(null''am(array('id' => $id), $attributes));
        }
        
        return 
$html;
    }
    
    
/**
     * Renders the defined timeline
     *
     * @return string SCRIPT element containing javascript code to render the timeline
     */
    
public function end() {
        
$code 'SimileAjax.History.enabled = false;';
        
        if (!empty(
$this->clickEvent)) {
            
$code $this->clickEvent;
        }
        
        foreach (
$this->bandGroups as $group => $data) {
            
$code .= "var eventSource_$group = new Timeline.DefaultEventSource();\n";
        }
        
        
$code .= $this->createBandInfo();
        
$code .= 'var timeline = Timeline.create(document.getElementById("' $this->divId '"), bandInfo);' "\n";
        unset(
$this->divId);
        
        foreach (
$this->bandGroups as $bandGroup => $data) {
            if (!empty(
$this->bandGroups[$bandGroup]['EventSource'])) {
                
$code .= 'timeline.loadJSON("' $this->bandGroups[$bandGroup]['EventSource'] . '", function(data, url) { eventSource_' $bandGroup '.loadJSON(data, url); });' "\n";
            }
            unset(
$this->bandGroups[$bandGroup]);
        }
        
        
$this->timeSyncs = array();
        
$this->bands = array();
        
$this->bandCount 0;
        
        return 
$this->Javascript->codeBlock($code);
    }
    
    
/**
     * Set a custom event handler for click event on timeline
     *
     * @param string $javascript
     */
    
public function setClickEvent($javascript '') {
        
$this->clickEvent 'Timeline.DurationEventPainter.prototype._showBubble = function(x, y, evt) { ' $javascript ' }';
    }
    
    
/**
     * Set the URL to fetch data from
     *
     * @param mixed $url url to JSON data
     * @param mixed $bandGroup named group of bands to share datasource with
     */
    
public function setDataSource($url$bandGroup 'main') {
        
$url $this->Html->url($url);
        foreach ((array) 
$bandGroup as $group) {
            
$this->bandGroups[$group]['EventSource'] = $url;
        }
    }
    
    
/**
     * Renders JSON encoded event data
     *
     * @param array $data
     * @param array $defaultEventAttributes
     * @param array $xpaths XPaths to Event attributes in $data
     * @return string JSON encoded timeline data
     */
    
public function renderJSON($data = array(), $defaultEventAttributes = array(), $xpaths = array()) {
        
$events = array();
        foreach (
$data as $key => $row) {
            
$events[$key] = $defaultEventAttributes;
            
$start Set::extract($row$xpaths['start']);
            
$events[$key]['start'] = $start[0];
            foreach (
$xpaths as $field => $path) {
                
$arr Set::extract($row$path);
                
$events[$key][$field] = $arr[0];
            }
        }
        
        
$returnArr = array('dateTimeFormat' => 'iso8601''events' => $events);
        
        return 
$this->Javascript->object($returnArr);
    }
    
    
/**
     * Generate the javascript code for the BandInfo structure, including setting which bands to synchronize
     *
     * @return string Javascript code
     */
    
private function createBandInfo() {
        
$bandInfo 'var bandInfo = [';
        
        foreach (
$this->bands as $band => $info) {
            
$bandInfo .= 'Timeline.createBandInfo({';
            
            foreach (
$info as $key => $value) {
                
$bandInfo .= $key ': ' $value ', ';
            }
            
            
$bandInfo substr($bandInfo0, -2);
            
$bandInfo .= '}), ';
        }
        
        if (
strlen($bandInfo) >= 18) {
            
$bandInfo substr($bandInfo0, -2);
        }
        
$bandInfo .= "];\n";
        
        foreach (
$this->timeSyncs as $timeSync) {
            
$bandCount count($timeSync);
            if (
$bandCount 1) {
                for (
$i 1$i $bandCount$i++) {
                    
$bandInfo .= 'bandInfo[' $timeSync[$i] . '].syncWith = ' $timeSync[$i 1] . '; bandInfo[' $timeSync[$i] . "].highlight = true;\n";
                }
            }
        }
        return 
$bandInfo;
    }
}
?>

Vendor code

http://code.google.com/p/simile-widgets/source/browse/#svn/timeline/trunk