FrxSVGGraph.inc 21.9 KB
Newer Older
1
2
3
4
5
6
7
8
<?php
/**
 * @file FrxSVGGraph
 * php SVG Graph generator
 *
 * @author davidmetzler
 *
 */
9
10
include_once('FrxCrosstab.inc');
include_once('FrxTable.inc');
11
class FrxSVGGraph extends FrxRenderer {
12
13
  private $graph;
  private $links;
14
  public $templateName = 'Graph (svg)';
15
  public $xy_data = FALSE;
16
  private $point_attributes;
17
18
  private $headers = array();
  private $weight;
19
20
21
  private $graphData;
  private $graphOptions;
  private $colors;
22
23
24
25
26
27
28
29
  private $unset_attrs = array(
    'base_type',
    'crosstab_columns',
    'content',
    'sections',
    'columns',
    'style',
  );
30
31
32

  // Place to indicate which fields are sourced from the data.
  private $field_sources = array();
33

34
  public function __construct() {
35
    parent::__construct();
36
    $library = forena_library_file('SVGGraph');
metzlerd's avatar
metzlerd committed
37
    require_once $library;
38
  }
39

40
41
42
  /**
   * Re-architect the data into something that the graphing engine can work with
   */
43
  public function generateGraphData(&$data, $series, $key) {
44
45

    // Default controling attributes
46
    $dim_headers = array($key);
47
    $legend = array();
48
    $this->graphOptions['structure']['value'] = array();
David Metzler's avatar
David Metzler committed
49
50
    foreach($series as $col) {
      $this->graphOptions['structure']['value'][] = trim("$col", '{}');
51
52
    }

David Metzler's avatar
David Metzler committed
53

54
55
56
57
58
    $this->graphData = array();
    foreach ($data as $row) {
      Frx::Data()->push($row, '_row');
      $trow = array();
      // Base group
59
      $trow['key'] =  $this->teng->replace($key, TRUE);;
60
61
62
      // Dimensions
      foreach($series as $col) {
          $val = $this->teng->replace($col, TRUE);
63
          if ($val != '' && $val !==NULL) $trow[trim("$col", '{}')] = $val;
64
      }
65
      foreach($this->field_sources as $k => $src) {
66
        $trow[$k] = $this->teng->replace($src, TRUE);
67
      }
68
69
70
71
      if(isset($this->field_sources['legend_entries'])) { 
        $legend_str = $trow['legend_entries']; 
        $legend[$legend_str] = $legend_str;  
      }
72
73
74
      Frx::Data()->pop();
      $this->graphData[] = $trow;
    }
75
76
77
78
79
    
    // Deal with rare case where legend are supposed to come from data
    if (isset($this->field_sources['legend_entries'])) {
      $this->graphOptions['legend_entries'] = array_values($legend);
    }
80
81
  }

82
83
84
85

   /**
   * Re-architect the data into something that the graphing engine can work with
   */
86
  public function generateGroupGraphData(&$block_data, $group, $series, $key, $dim) {
87
88
89
90
91
92
    $dim_headers = array();
    $dim_rows = array();
    $dim_columns = array();
    $group_headers = array();
    $dim_values = array();
    $rows = array();
93
    $legend = array(); 
94
    $data = $this->frxReport->group($block_data, $group);
95
    $this->graphOptions['structure'] = array('key' => $group);
96
97
98
99
100
101
102
103
104
105
106
107
108

    foreach ($data as $gk => $group_rows) {
      $row_copy = array_values($group_rows);
      $dims = $this->frxReport->group($group_rows, $dim);
      $rows[$gk] = $group_rows[0];
      foreach($dims as $dk=>$r) {
        $dims = array_values($r);
        $dim_values[$dk] = $dk;
        $dim_rows[$gk][$dk] = $r[0];
      }
    }

    // Default controling attributes
109
    $dim_headers = array($key);
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
    $dim_columns = $series;

    // Add the dimension headers.
    foreach ($dim_values as $dk) {
      foreach ($dim_headers as $i => $col) {
        $cell = '';
        if (count($dim_columns) > 1) {
          $cell = $i ? $col : $dk . ' ' . $col;
        }
        $hrow []  = $cell;
      }
    }

    foreach($dim_values as $dk) {
      foreach($dim_columns as $col) {
125
        $structure_idx = $dk . $col; 
126
127
        $this->graphOptions['structure']['value'][] = $structure_idx;
        foreach($this->field_sources as $k=>$fld) {
128
          $structure_idx = $dk . $k; 
129
130
          $this->graphOptions['structure'][$k][] = $structure_idx;
        }
131
132
133
134
      }
    }

    $this->graphData = array();
135
    $gkey = '';
136
137
138
139
140
    foreach ($rows as $k=>$row) {
      Frx::Data()->push($row, '_group');
      $trow = array();
      // Base group

141
      $gkey = $this->teng->replace($group, TRUE);
142
      $trow[$group] = $gkey;
143
      Frx::Data()->pop();
144
145
146
147
148
149
      // Dimensions
      $dim_data = $dim_rows[$k];
      foreach($dim_values as $dk) {
        $dim_row = isset($dim_data[$dk]) ? $dim_data[$dk] : array();
        frx::Data()->push($dim_row, '_dim');
        foreach($dim_columns as $col) {
150

151
          $cell = $col;
152
          $val = $this->teng->replace($col, TRUE);
153
154
          //if(!$val) $val = 0;
          if ($val !== '' && $val !== NULL) $trow[$dk . $col] = $val;
155
          foreach($this->field_sources as $fk => $src) {
156
            $trow[$dk . $fk] = $this->teng->replace($src, TRUE);
157
158
159
160
161
162
            
            if (isset($this->field_sources['legend_entries'])) { 
              $legend_str = $this->teng->replace($this->field_sources['legend_entries']); 
              $legend[$legend_str] = $legend_str; 
            }
            
163
          }
164
        }
165
166
        
        
167
168
169
170
171
        frx::Data()->pop();
      }
      $this->graphData[] = $trow;

    }
172
173
174
175
176
177
178
    
    
    // Deal with rare case where legend are supposed to come from data
    if (isset($this->field_sources['legend_entries'])) {

      $this->graphOptions['legend_entries'] = array_values($legend);
    }
179

180
181
182
183
184
185
    return $this->graphData;
  }

  /**
   * Add column for cross tabs.
   */
186
187
188
189
190
191
192
193
194
195
  private function addColumn($type, $token, $label,  &$config) {
    $key = trim($token, '{}');
    $this->weight++;
    $config['crosstab_columns'][$key] = array(
        'contents' => $token,
        'label' => $label,
        'type' => $type,
        'weight' => $this->weight,
    );
  }
196
197
198
199

  /**
   * Derive config variables from graph.
   */
200
  public function scrapeConfig() {
201
202
203
204
205
206
    $this->weight = 0;
    $attrs = array();
    $svg = '';
    $nodes =  $this->reportDocNode->xpath('//svg');
    if ($nodes) {
      $svg = $nodes[0];
207
208
      $config = $this->mergedAttributes($svg);
    }
209
210

    // Determine graph type
211
    $graph_type =  isset($config['type']) ? $config['type'] : 'BarGraph';
212

213
214
    $types = $this->graphTypes();
    $types = array_change_key_case($types);
215
    $config['base_type'] = $types[strtolower($graph_type)]['type'];
216

217
    $this->extractTemplateHTML($this->reportDocDomNode, $config, array('svg', 'table'));
218
219
220
221
222
223
224
225
226
227
228
    if (!isset($config['key'])) $config['key'] =  @$config['label'];
    if (!isset($config['key'])) $config['key'] = @$config['seriesx'];
    $key = $config['key'];
    $dim = @$config['dim'];
    $series = @(array)$config['series'];

    if ($key) {
      $keys = explode(' ', $key);
      foreach ($keys as $k) {
        $this->addColumn('heading', $k, trim($k, '{}'), $config);
      }
229
    }
230
231
232
233
    if ($series) foreach ($series as $col) {
      $this->addColumn('value',$col , trim($col, '{}'), $config);
    }

234
235
236
237
238
239
240
    // Get the data cells
    if ($dim) {
      $dims = (array)$dim;
      foreach($dims as $dim) {
        $this->addColumn('crosstab', $dim, trim($dim, '{}'), $config);
      }
    }
241
    $config['style'] = $config['type'];
242
243
244
    return $config;
  }

245
246
247
248
  /**
   * Render the graph.
   * @return Ambigous <string, mixed>
   */
249
250
251
252
253
  public function render() {
    // Get data from source
    $output = '';
    $options = array();
    $series = array();
254
255
256
    $label = '';
    $links = '';
    $dim='';
257
258
    $seriesx = array();
    $attributes = $this->mergedAttributes();
259
    // Default in xpath for backward compatibility
260
261
262
263
    // Legacy options.  New charts should be generated using Frx:attribute syntax
    if (isset($attributes['options'])) {
      parse_str($attributes['options'], $options);
      unset($attributes['options']);
264
265
266
      foreach ($options as $key => $value) {
        $options[$key] = $this->teng->replace($value, TRUE);
      }
267
268
269
270
271
272
      $options  = array_merge($attributes, $options);
    }
    else {
      $options = $attributes;
    }

273
274
275
276
277
    // Main Graphing options
    $path = $this->extract('path', $options);
    if (!$path) $path = $this->extract('xpath', $options); //Deprecated
    if (!$path) $path = '*';
    $group = $this->extract('group', $options);
278
    $series = @(array)$attributes['series'];
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
    $label = @$attributes['label'];
    $key = @$attributes['key'];
    if (!$key) $key = @$attributes['label']; //Backward compatibility for old graphs.
    $options['key'] = $key;
    $dim = $this->extract('dim', $options);
    $type = $this->extract('type', $options);
    if (!$key) $key = @$options['seriesx']; // Deprecated

    // Determine basic data to iterate.
    $data = Frx::Data()->currentContext();
    if (is_object($data)) {
      if (method_exists($data, 'xpath')) {
        $nodes = $data->xpath($path);
      }
      else {
        $nodes = $data;
      }
296
    }
297
298
    else {
      $nodes = (array)$data;
299
300
    }

301
302
    // Force structured data
    $options['structured_data'] = TRUE;
303
304
305
    $options['structure'] = array('key' => 'key');

    // Default in american colour;
306
307
    $this->field_sources = array();
    if (isset($options['color'])) $options['colour'] = $options['color'];
308
309

    // Find out data that is designed to be sepecif to series.
310
    $this->field_sources = array();
311
    $i= 0;
312
313
314
    foreach ($options as $fk => $opt) {
      if ($fk != 'value' && $fk != 'key' && $opt && !is_array($opt) && strpos($options[$fk], '{')!==FALSE) {
        $this->field_sources[$fk] = $opt;
315
        $options['structure'][$fk] = $fk;
316
317
      }
    }
318
319
    if (isset($attributes['height'])) $options['height'] = $this->teng->replace($attributes['height'], TRUE);
    if (isset($attributes['width'])) $options['width'] = $this->teng->replace($attributes['width'], TRUE);
320
321
322
323
    if (isset($options['legend_entries']) && !isset($options['label']) && !isset($options['show_labels'])) {
      $options['show_labels'] = FALSE;
    }

324

325
326
    $this->graphOptions = $options;

327
    if ($group && $dim) {
328
      $this->generateGroupGraphData($nodes, $group, $series, $key, $dim);
329
330
    }
    else {
331
      $this->generateGraphData($nodes, $series, $key);
332
    }
333
    
334
    if (isset($this->graphOptions['legend_entries']) && !is_array($this->graphOptions['legend_entries'])) {
335
      $this->graphOptions['legend_entries'] = explode('|', $this->graphOptions['legend_entries']);
336
    }
337
    
338
    if ($data) {
339
      $output = $this->renderChart($type, $data);
340
    }
341

342
343
344
    return $output;
  }

metzlerd's avatar
metzlerd committed
345
346
  static function graphTypes() {
    return array(
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
      'BarGraph' => array('type' => 'Bar Graph', 'style' => 'Simple' ),
      'Bar3DGraph' => array('type' => 'Bar Graph', 'style' => '3D' ),
      'StackedBarGraph' => array('type' => 'Bar Graph', 'style' => 'Stacked'),
      'GroupedBarGraph' => array('type' => 'Bar Graph', 'style' => 'Grouped'),
      'CylinderGraph' => array('type' => 'Bar Graph', 'style' => 'Cylinder'),
      'StackedCylinderGraph' => array('type' => 'Bar Graph', 'style' => 'Stacked Cylinder'),
      'GroupedCylinderGraph' =>  array('type' => 'Bar Graph', 'style' => 'Grouped Cylinder'),
      'PieGraph' => array('type' => 'Pie Chart', 'style' => 'Simple'),
      'Pie3DGraph' => array('type' => 'Pie Chart', 'style' => '3D'),
      'HorizontalBarGraph' => array('type' => 'Bar Graph', 'style' => 'Horizontal'),
      'LineGraph' =>array('type' => 'Line Graph', 'style' => 'Simple'),
      'MultiLineGraph' => array('type' => 'Line Graph', 'style' => 'Multi'),
      'ScatterGraph' => array('type' => 'Scatter Plot', 'style' => 'Simple', 'xaxis' => TRUE),
      'MultiScatterGraph' => array('type' => 'Scatter Plot', 'style' => '3D', 'xaxis' => TRUE),
      'RadarGraph' => array('type' => 'Radar Graph', 'style' => 'Simple'),
      'MultiRadarGraph' => array('type' => 'Radar Graph', 'style' => 'Multi'),
metzlerd's avatar
metzlerd committed
363
364
365
366
367
368
369
370
371
372
373
374
    );
  }

  static function graphOptions() {
    $data = FrxSVGGraph::graphTypes();
    foreach($data as $key => $value) {
      $type[$value['type']] = $value['type'];
      $style[$value['type']][$key] = $value['style'];
    }
    return array('types' => $type, 'styles' => $style);
  }

375
  function renderChart($type, $data ) {
376
377
    $options['width'] = @$options['width'] ? $options['width'] : 600;
    $options['height'] = @$options['height'] ? $options['height'] : 400;
metzlerd's avatar
metzlerd committed
378
379
380
381
382
    $type = strtolower($type);
    // Legacy sustitions for backcward compatibility.
    if ($type == 'piechart') $type = 'piegraph';
    if ($type == 'scatterplot') $type = 'scattergraph';
    if ($type == 'multiscatterplot') $type = 'multiscattergraph';
metzlerd's avatar
metzlerd committed
383

metzlerd's avatar
metzlerd committed
384
385
386
387
388
389
    // Newly defined types
    $graph_types = FrxSVGGraph::graphTypes();

    // Build map for array types.
    $lower_graphs_types = array_change_key_case($graph_types);
    $graph_classes = array_combine(array_keys($lower_graphs_types), array_keys($graph_types));
390

metzlerd's avatar
metzlerd committed
391

metzlerd's avatar
metzlerd committed
392
393
    if ($data && isset($graph_classes[$type])) {
      $class = $graph_classes[$type];
394
      $output = $this->renderGraph($class);
395
    }
metzlerd's avatar
metzlerd committed
396
    return $output;
397
398
399
  }


400
  function renderGraph($type) {
401
    static $jsinc = '';
402

403
404
405
    $options = $this->graphOptions;
    $data = $this->graphData;

406
    if (!isset($options['scatter_2d']) && ($type == 'ScatterGraph' || $type=='MultiScatterGraph') && $this->xy_data && !isset($options['scatter_2d'])) {
407
408
      $options['scatter_2d'] = TRUE;
    }
metzlerd's avatar
metzlerd committed
409
    else {
410
      $options['scatter_2d'] = (bool) @$options['scatter_2d'];
metzlerd's avatar
metzlerd committed
411
    }
412
413
414
415
416
    $width = (@$options['width']) ? @$options['width'] : 600;
    $height = (@$options['height']) ? @$options['height'] : 400;
    $graph = new SVGGraph($width, $height, $options);
    $this->graph = $graph;
    $graph->Values($data);
417
    if (isset($options['colour']) && is_array($options['colour'])) {
418
      $graph->Colours($options['colour']);
419
    }
420
    // Generate the graph
421
422
423
424
425
    $output = $graph->Fetch($type, FALSE);
    // Add a viewbox to be compatible with Prince PDF generation.
    $output = str_replace('<svg width', "<svg viewBox='0 0 $width $height' width", $output);
    $javascript = '/<script[^>]*?>.*?<\/script>/si';
    $noscript = '';
426
    if (!$jsinc && $this->format == 'web') {
427
      if (@!$options['no_js']) {
428
429
430
        $js =  $graph->FetchJavascript();
        $output .= $js;
      }
431
      $jsinc = TRUE;
432
433
434
435
    }
    return $output;
  }

436
437
438
439
440
441
442
443
444
445
446
447
448
449
  /**
   * Generate series from crosstab.
   * @param unknown $config
   */
  private function seriesFromColumns(&$config) {
    $series = array();
    foreach ($config['crosstab_columns'] as $key => $col) {
      if ($col['type'] == 'value') {
        $series[] = '{' . $key . '}';
      }
    }
    $config['series'] = $series;
  }

450
451
452
  private function keyFromColumns(&$config) {
    $key = '';
    foreach ($config['crosstab_columns'] as $k => $col) {
453
      if ($col['type'] == 'heading') {
454
        $key = trim($key . ' {' . $k . '}');
455
456
      }
    }
457
    $config['key'] = $key;
458
459
  }

460
  public function configForm($config) {
461
    $form = array();
David Metzler's avatar
David Metzler committed
462
463
    $graph_options = FrxSVGGraph::graphOptions();
    $graph_types = FrxSVGGraph::graphTypes();
464
    $gt = array_change_key_case($graph_types);
465

466
    $type = @$config['style'];
467
468
469
470
471
472
    if (!isset($config['base_type'])) {
      $base_type = $type ? $gt[strtolower($type)]['type'] : 'Bar Graph';
    }
    else {
      $base_type = $config['base_type'];
    }
473
    $styles = $graph_options['styles'][$base_type];
David Metzler's avatar
David Metzler committed
474
    $types = $graph_options['types'];
475
476
    $form = parent::configForm($config);

477
    $form['base_type'] = array(
David Metzler's avatar
David Metzler committed
478
479
480
        '#type' => 'select',
        '#title' => t('Graph Type'),
        '#options' => $graph_options['types'],
481
        '#default_value' => $base_type,
482
        '#ajax' => $this->configAjax(),
David Metzler's avatar
David Metzler committed
483
484
    );

485
    $form['style'] = array(
David Metzler's avatar
David Metzler committed
486
487
488
        '#type' => 'select',
        '#title' => t('Style'),
        '#options' => $styles,
489
        '#default_value' => $type,
490
        '#ajax' => $this->configAjax(),
David Metzler's avatar
David Metzler committed
491
492
    );

493
    $form['gen_table'] = array(
David Metzler's avatar
David Metzler committed
494
495
496
        '#type' => 'checkbox',
        '#title' => t('Include data table with graph'),
        '#default_value' => @$config['gen_table'],
497
        '#ajax' => $this->configAjax(),
David Metzler's avatar
David Metzler committed
498
499
500
    );


501
502
503
504
505
506
507
508
509
510
511
    $this->weight_sort($config['crosstab_columns']);
    $types = array('heading' => t('Label'), 'crosstab' => t('Crosstab'), 'value' => 'Value', 'ignore' => t('Ignore'));
    $form['crosstab_columns'] = array('#theme' => 'forena_element_draggable',   '#draggable_id' => 'FrxCrosstab-columns');
    foreach ($config['crosstab_columns'] as $key => $col) {
      $ctl = array();
      $ctl['label'] = array(
          '#type' => 'textfield',
          '#size' => 30,
          '#title' => t('Label'),
          '#default_value' => $col['label'],
      );
David Metzler's avatar
David Metzler committed
512

513
514
515
516
517
      $ctl['contents'] = array(
          '#type' => 'textfield',
          '#size' => '30',
          '#title' => t('Data'),
          '#default_value' => $col['contents'],
David Metzler's avatar
David Metzler committed
518
519
      );

520
521
522
523
524
525
526
      $ctl['type'] = array(
          '#type' => 'radios',
          '#title' => t('Type'),
          '#default_value' => $col['type'],
          '#options' => $types,
          '#ajax' => $this->configAjax()
      );
David Metzler's avatar
David Metzler committed
527

528
529
530
531
532
      $ctl['weight'] = array(
          "#type" => 'weight',
          '#title' => t('Weight'),
          '#delta' => 50,
          '#default_value' => $col['weight'],
David Metzler's avatar
David Metzler committed
533
534
      );

535
      $form['crosstab_columns'][$key] = $ctl;
David Metzler's avatar
David Metzler committed
536
537
    }

538
539
540
541
542
    $form['label'] = array(
    	'#type' => 'textfield',
      '#title' => t('Graph Label'),
      '#default_value' => @$config['label'],
    );
543
544

    $form['link'] = array(
David Metzler's avatar
David Metzler committed
545
        '#type' => 'textfield',
546
        '#title' => t('Link Url'),
David Metzler's avatar
David Metzler committed
547
548
549
        '#default_value' => @$config['link'],
    );

550
551
552
    $form['legend_entries'] = array(
        '#type' => 'textfield',
        '#title' => t('Legend'),
553
        '#default_value' => @$config['legend_entries'],
554
555
556
557
558
559
560
561
562
    );

    $form['tooltip'] = array(
        '#type' => 'textfield',
        '#title' => t('Tool Tip'),
        '#default_value' => @$config['tooltip'],
    );


563
    return $form;
David Metzler's avatar
David Metzler committed
564
565
  }

566
567
568
569
570
571
572
573
574
575
576
  public function configValidate(&$config) {
    $type = $config['style'];
    $base_type = $config['base_type'];
    $graph_options = $this->graphOptions();
    if(!array_key_exists($type, $graph_options['styles'][$base_type])) {
      $styles = array_keys($graph_options['styles'][$base_type]);
      $config['type'] = $config['style'] = array_shift($styles);
    }
    $config['type'] = $config['style'];
  }

577
  public function generate($xml, &$config) {
578
579
    $config['class'] = get_class($this);
    $media = 'FrxSVGGraph';
David Metzler's avatar
David Metzler committed
580
    $div = $this->blockDiv($config);
581
    $this->removeChildren($div);
582
    $attrs  = array();
583
    // PUt on the header
584
585
586
587
    if (isset($config['header']['value'])) {
      $header = $this->extract('header', $config);
      $this->addFragment($div, $header['value']);
    }
588

589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
    // Determine columns and make sure we represent them all
    $found_columns = $this->columns($xml);
    if (!$found_columns) {
      $found_columns = $this->columns($xml, '/*');
    }

    $numeric_columns = $this->numeric_columns;
    $new_columns = @$config['crosstab_columns'] ? FALSE : TRUE;
    foreach ($found_columns as $column => $label) {
      $token = '{' . $column . '}';
      if ($new_columns) {
        $type = isset($numeric_columns[$column]) ? 'value' : 'heading';
      } else {
        $type = 'ignore';
      }
      if (!isset($config['crosstab_columns'][$column])) {
        $this->addColumn($type, '{' . $column . '}', $column, $config);
      }
    }

    // Generate the grouping row
    $group = '';
    $dim = array();
612

613
    foreach($config['crosstab_columns'] as $col) {
614
      if ($col['type'] == 'heading') $group[] = $col['contents'];
615
616
      if ($col['type'] == 'crosstab') $dim = $col['contents'];
    }
617
    if ($group) $config['group'] = is_array($group) ?  implode(' ', $group) : $group;
618
    if ($dim) $config['dim'] = $dim;
619
620
621



622
623
624
625
626
    $this->seriesFromColumns($config);
    $this->keyFromColumns($config);

    // Clean colors
    if (isset($config['colors'])) foreach ($config['colors'] as $i => $color) if (!$color) unset($color[$i]);
627

628
629
630
631
632
633
    $type = $this->extract('type', $config);
    if (!$type) $type = 'Bar Graph';

    $gen_table = $this->extract('gen_table', $config);
    $graph_options = FrxSVGGraph::graphOptions();
    // LImit the config
634
635
636
637
    $frxattrs = $config;
    // Unset common option configurations?
    foreach($this->unset_attrs as $k) unset($frxattrs[$k]);

638
    $frxattrs = $this->arrayAttributes($frxattrs);
639
640


641
642
    $frxattrs['renderer'] = 'FrxSVGGraph';
    $frxattrs['type'] = $type;
David Metzler's avatar
David Metzler committed
643

644
645
646
647
648
649
650
651
652
653
654
    $svg = $this->setFirstNode($div, 2, 'svg',  NULL, NULL,  $frxattrs);

    if (isset($config['footer']['value'])) $this->addFragment($div, $config['footer']['value']);
    if ($gen_table) {
      if($group && $dim) {
        $this->generateCrossTab($xml, $config, $div, $group, $dim);
      }
      else {
        $this->generateTable($xml, $config, $div);
      }
    }
metzlerd's avatar
metzlerd committed
655

656
  }
David Metzler's avatar
David Metzler committed
657

658
659
660
661
662
663
664
665
666
667
668
669
  /**
   * Generate a basic table.
   * @param unknown $xml
   * @param unknown $config
   * @param unknown $div
   */
  function generateTable($xml, &$config, &$div) {
    $attrs = array('foreach' => '*');
    $table = $this->setFirstNode($div, 4, 'table');
    if (@$config['caption']) {
      $this->setFirstNode($table, 6, 'caption', $config['caption']);
    }
670

671
672
673
674
    $thead = $this->setFirstNode($table, 6, 'thead');
    $throw = $this->setFirstNode($thead, 8, 'tr');
    $tbody = $this->setFirstNode($table, 6, 'tbody');
    $tdrow = $this->setFirstNode($tbody, 8, 'tr', NULL, array(),$attrs);
David Metzler's avatar
David Metzler committed
675

676
677
678
    if (isset($config['crosstab_columns'])) foreach ($config['crosstab_columns'] as $key => $col) if ($col['type']!='ignore') {
      $this->addNode($throw, 10, 'th', $col['label']);
      $this->addNode($tdrow, 10, 'td', $col['contents']);
David Metzler's avatar
David Metzler committed
679
    }
680
  }
681

682
683
684
685
686
687
688
689
690
  /**
   * Generate a crosstab table.
   * @param unknown $xml
   * @param unknown $config
   * @param unknown $div
   * @param unknown $group
   * @param unknown $dim
   */
  function generateCrossTab($xml, &$config, &$div, $group, $dim) {
691
    $attrs = array(); 
692
    $table_frx['renderer'] = 'FrxCrosstab';
693
    $table_frx['group'] = is_array($group) ? implode(' ', $group) : $group;
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
    $table_frx['dim'] = $dim;
    $table = $this->setFirstNode($div, 4, 'table', NULL, $attrs, $table_frx);
    $thead = $this->setFirstNode($table, 6, 'thead');
    $throw = $this->setFirstNode($thead, 8, 'tr');
    $tbody = $this->setFirstNode($table, 6, 'tbody');
    $tdrow = $this->setFirstNode($tbody, 8, 'tr', NULL, NULL, $attrs);
    if ($config['crosstab_columns']) foreach ($config['crosstab_columns'] as $key => $col) if ($col['type']!=='ignore') {
      if ($col['type']=='heading') {
        $tag = 'th';
      }
      else {
        $tag = 'td';
      }
      if ($col['type'] != 'crosstab') {
        $this->addNode($throw, 10, $tag, $col['label']);
        $this->addNode($tdrow, 10, $tag, $col['contents']);
      }
    }
David Metzler's avatar
David Metzler committed
712

713
  }
714
715

}