lib/TWiki/Plugins/TablePlugin/Core.pm
author Colas Nahaboo <colas@nahaboo.net>
Sat, 26 Jan 2008 15:50:53 +0100
changeset 0 414e01d06fd5
child 1 e2915a7cbdfa
permissions -rw-r--r--
RELEASE 4.2.0 freetown
colas@0
     1
# Plugin for TWiki Enterprise Collaboration Platform, http://TWiki.org/
colas@0
     2
#
colas@0
     3
# Copyright (C) 2001-2003 John Talintyre, jet@cheerful.com
colas@0
     4
# Copyright (C) 2001-2004 Peter Thoeny, peter@thoeny.org
colas@0
     5
# Copyright (C) 2005-2006 TWiki Contributors
colas@0
     6
#
colas@0
     7
# This program is free software; you can redistribute it and/or
colas@0
     8
# modify it under the terms of the GNU General Public License
colas@0
     9
# as published by the Free Software Foundation; either version 2
colas@0
    10
# of the License, or (at your option) any later version. For
colas@0
    11
# more details read LICENSE in the root of this distribution.
colas@0
    12
#
colas@0
    13
# This program is distributed in the hope that it will be useful,
colas@0
    14
# but WITHOUT ANY WARRANTY; without even the implied warranty of
colas@0
    15
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
colas@0
    16
# GNU General Public License for more details, published at
colas@0
    17
# http://www.gnu.org/copyleft/gpl.html
colas@0
    18
#
colas@0
    19
# As per the GPL, removal of this notice is prohibited.
colas@0
    20
colas@0
    21
use strict;
colas@0
    22
colas@0
    23
package TWiki::Plugins::TablePlugin::Core;
colas@0
    24
colas@0
    25
use Time::Local;
colas@0
    26
colas@0
    27
use vars qw( $translationToken
colas@0
    28
  $insideTABLE $tableCount @curTable $sortCol $maxSortCols $requestedTable $up
colas@0
    29
  $sortTablesInText $sortAttachments $currTablePre $sortColFromUrl
colas@0
    30
  $tableWidth @columnWidths
colas@0
    31
  $tableBorder $tableFrame $tableRules $cellPadding $cellSpacing $cellBorder
colas@0
    32
  @headerAlign @dataAlign $vAlign $headerVAlign $dataVAlign
colas@0
    33
  $headerBg $headerBgSorted $headerColor $sortAllTables $twoCol @dataBg @dataBgSorted @dataColor
colas@0
    34
  @isoMonth
colas@0
    35
  $headerRows $footerRows
colas@0
    36
  $upchar $downchar $diamondchar $url
colas@0
    37
  @isoMonth %mon2num $initSort $initDirection $currentSortDirection
colas@0
    38
  @rowspan $pluginAttrs $prefsAttrs $tableId $tableSummary $tableCaption
colas@0
    39
  $iconUrl $unsortEnabled
colas@0
    40
  %sortDirection %columnType
colas@0
    41
  %cssAttrs %defaultCssAttrs $didWriteDefaultStyle
colas@0
    42
);
colas@0
    43
colas@0
    44
BEGIN {
colas@0
    45
    $translationToken = "\0";
colas@0
    46
    $currTablePre     = '';
colas@0
    47
    $upchar           = '';
colas@0
    48
    $downchar         = '';
colas@0
    49
    $diamondchar      = '';
colas@0
    50
    @isoMonth         = (
colas@0
    51
        'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
colas@0
    52
        'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
colas@0
    53
    );
colas@0
    54
    {
colas@0
    55
        my $count = 0;
colas@0
    56
        %mon2num = map { $_ => $count++ } @isoMonth;
colas@0
    57
    }
colas@0
    58
    %sortDirection = ( 'ASCENDING', 0, 'DESCENDING', 1, 'NONE', 2 );
colas@0
    59
    %columnType = (
colas@0
    60
        'TEXT',   'text',   'DATE',      'date',
colas@0
    61
        'NUMBER', 'number', 'UNDEFINED', 'undefined'
colas@0
    62
    );
colas@0
    63
colas@0
    64
    # the maximum number of columns we will handle
colas@0
    65
    $maxSortCols = 10000;
colas@0
    66
    $iconUrl =
colas@0
    67
        TWiki::Func::getPubUrlPath() . '/'
colas@0
    68
      . TWiki::Func::getTwikiWebname()
colas@0
    69
      . '/TWikiDocGraphics/';
colas@0
    70
    $unsortEnabled        = 1;    # if true, table columns can be unsorted
colas@0
    71
    $didWriteDefaultStyle = 0;
colas@0
    72
    my %defaultCssAttrs = ();
colas@0
    73
}
colas@0
    74
colas@0
    75
sub _setDefaults {
colas@0
    76
    $sortAllTables  = $sortTablesInText;
colas@0
    77
    $tableBorder    = 1;
colas@0
    78
    $tableFrame     = '';
colas@0
    79
    $tableRules     = '';
colas@0
    80
    $cellSpacing    = '';
colas@0
    81
    $cellPadding    = '';
colas@0
    82
    $cellBorder     = '';
colas@0
    83
    $tableWidth     = '';
colas@0
    84
    $headerRows     = 1;
colas@0
    85
    $footerRows     = 0;
colas@0
    86
    $vAlign         = '';
colas@0
    87
    $headerVAlign   = '';
colas@0
    88
    $dataVAlign     = '';
colas@0
    89
    $headerBg       = '#6b7f93';
colas@0
    90
    $headerBgSorted = '';
colas@0
    91
    $headerColor    = '#ffffff';
colas@0
    92
    $tableId        = '';
colas@0
    93
    $tableSummary   = '';
colas@0
    94
    $tableCaption   = '';
colas@0
    95
    @columnWidths   = ();
colas@0
    96
    @headerAlign    = ();
colas@0
    97
    @dataAlign      = ();
colas@0
    98
    @dataBg         = ( '#ecf2f8', '#ffffff' );
colas@0
    99
    @dataBgSorted   = ();
colas@0
   100
    @dataColor      = ();
colas@0
   101
colas@0
   102
    undef $initSort;
colas@0
   103
colas@0
   104
    # Preferences setting
colas@0
   105
    # It seems overkill to redo this every time!
colas@0
   106
    my %pluginParams   = TWiki::Func::extractParameters($pluginAttrs);
colas@0
   107
    my %prefsParams    = TWiki::Func::extractParameters($prefsAttrs);
colas@0
   108
    my %combinedParams = ( %pluginParams, %prefsParams );
colas@0
   109
    _parseParameters( 1, 'default', %combinedParams );
colas@0
   110
}
colas@0
   111
colas@0
   112
# Table attributes defined as a Plugin setting, a preferences setting
colas@0
   113
# e.g. in WebPreferences or as a %TABLE{...}% setting
colas@0
   114
sub _parseParameters {
colas@0
   115
    my ( $useCss, $writeDefaults, %params ) = @_;
colas@0
   116
colas@0
   117
    return '' if !keys %params;
colas@0
   118
colas@0
   119
    %cssAttrs = ();
colas@0
   120
colas@0
   121
    my $tmp;
colas@0
   122
colas@0
   123
    $tmp = $params{id};
colas@0
   124
    if ( defined $tmp && $tmp ne '' && $tmp ne $tableId ) {
colas@0
   125
        $tableId = $tmp;
colas@0
   126
    }
colas@0
   127
    else {
colas@0
   128
        $tableId = 'table' . ( $tableCount + 1 );
colas@0
   129
    }
colas@0
   130
    $cssAttrs{tableId} = $tableId;
colas@0
   131
colas@0
   132
    # Defines which column to initially sort : ShawnBradford 20020221
colas@0
   133
    $tmp = $params{initsort};
colas@0
   134
    $initSort = $tmp if ($tmp);
colas@0
   135
colas@0
   136
    # Defines which direction to sort the column set by initsort :
colas@0
   137
    # ShawnBradford 20020221
colas@0
   138
    $tmp           = $params{initdirection};
colas@0
   139
    $initDirection = $sortDirection{'ASCENDING'}
colas@0
   140
      if ( defined $tmp && $tmp =~ /^down$/i );
colas@0
   141
    $initDirection = $sortDirection{'DESCENDING'}
colas@0
   142
      if ( defined $tmp && $tmp =~ /^up$/i );
colas@0
   143
colas@0
   144
    $tmp           = $params{sort};
colas@0
   145
    $tmp           = '0' if ( defined $tmp && $tmp =~ /^off$/oi );
colas@0
   146
    $sortAllTables = $tmp if ( defined $tmp && $tmp ne '' );
colas@0
   147
    
colas@0
   148
    # If EditTablePlugin is installed and we are editing a table, the CGI
colas@0
   149
    # parameter 'sort' is defined as "off" to disable all header sorting ((Item5135)
colas@0
   150
    my $cgi = TWiki::Func::getCgiQuery();
colas@0
   151
    $tmp = $cgi->param('sort');
colas@0
   152
    if ( defined $tmp && $tmp =~ /^off$/oi ) {
colas@0
   153
        undef $sortAllTables;
colas@0
   154
    }
colas@0
   155
colas@0
   156
    # If EditTablePlugin is installed and we are editing a table, the 
colas@0
   157
    # 'disableallsort' TABLE parameter is added to disable initsort and header
colas@0
   158
    # sorting in the table that is being edited. (Item5135)
colas@0
   159
    $tmp = $params{disableallsort};
colas@0
   160
    if ( defined $tmp && $tmp =~ /^on$/oi ) {
colas@0
   161
        undef $sortAllTables;
colas@0
   162
        undef $initSort;        
colas@0
   163
    }
colas@0
   164
colas@0
   165
    $tmp = $params{tableborder};
colas@0
   166
    if ( defined $tmp && $tmp ne '' ) {
colas@0
   167
        $tableBorder = $tmp if $tmp ne $tableBorder;
colas@0
   168
        if (
colas@0
   169
            $useCss
colas@0
   170
            && ( !defined $defaultCssAttrs{'tableBorder'}
colas@0
   171
                || $tmp ne $defaultCssAttrs{'tableBorder'} )
colas@0
   172
          )
colas@0
   173
        {
colas@0
   174
            $cssAttrs{tableBorder} = $tableBorder;
colas@0
   175
            $defaultCssAttrs{tableBorder} = $tableBorder if $writeDefaults;
colas@0
   176
        }
colas@0
   177
    }
colas@0
   178
colas@0
   179
    $tmp = $params{tableframe};
colas@0
   180
    if ( defined $tmp && $tmp ne '' && $tmp ne $tableFrame ) {
colas@0
   181
        $tableFrame = $tmp;
colas@0
   182
        if (
colas@0
   183
            $useCss
colas@0
   184
            && ( !defined $defaultCssAttrs{'tableFrame'}
colas@0
   185
                || $tmp ne $defaultCssAttrs{'tableFrame'} )
colas@0
   186
          )
colas@0
   187
        {
colas@0
   188
            $cssAttrs{tableFrame} = $tableFrame;
colas@0
   189
            $defaultCssAttrs{tableFrame} = $tableFrame if $writeDefaults;
colas@0
   190
        }
colas@0
   191
    }
colas@0
   192
colas@0
   193
    $tmp = $params{tablerules};
colas@0
   194
    if ( defined $tmp && $tmp ne '' && $tmp ne $tableRules ) {
colas@0
   195
        $tableRules = $tmp;
colas@0
   196
        if (
colas@0
   197
            $useCss
colas@0
   198
            && ( !defined $defaultCssAttrs{'tableRules'}
colas@0
   199
                || $tmp ne $defaultCssAttrs{'tableRules'} )
colas@0
   200
          )
colas@0
   201
        {
colas@0
   202
            $cssAttrs{tableRules} = $tableRules;
colas@0
   203
            $defaultCssAttrs{tableRules} = $tableRules if $writeDefaults;
colas@0
   204
        }
colas@0
   205
    }
colas@0
   206
colas@0
   207
    $tmp = $params{cellpadding};
colas@0
   208
    if ( defined $tmp && $tmp ne '' && $tmp ne $cellPadding ) {
colas@0
   209
        $cellPadding = $tmp;
colas@0
   210
        if (
colas@0
   211
            $useCss
colas@0
   212
            && ( !defined $defaultCssAttrs{'cellPadding'}
colas@0
   213
                || $tmp ne $defaultCssAttrs{'cellPadding'} )
colas@0
   214
          )
colas@0
   215
        {
colas@0
   216
            $cssAttrs{cellPadding} = $cellPadding;
colas@0
   217
            $defaultCssAttrs{cellPadding} = $cellPadding if $writeDefaults;
colas@0
   218
        }
colas@0
   219
    }
colas@0
   220
colas@0
   221
    $tmp = $params{cellspacing};
colas@0
   222
colas@0
   223
    # not used in CSS
colas@0
   224
    if ( defined $tmp && $tmp ne '' && $tmp ne $cellSpacing ) {
colas@0
   225
        $cellSpacing = $tmp;
colas@0
   226
    }
colas@0
   227
colas@0
   228
    $tmp = $params{cellborder};
colas@0
   229
    if ( defined $tmp && $tmp ne '' && $tmp ne $cellBorder ) {
colas@0
   230
        $cellBorder = $tmp;
colas@0
   231
        if (
colas@0
   232
            $useCss
colas@0
   233
            && ( !defined $defaultCssAttrs{'cellBorder'}
colas@0
   234
                || $tmp ne $defaultCssAttrs{'cellBorder'} )
colas@0
   235
          )
colas@0
   236
        {
colas@0
   237
            $cssAttrs{cellBorder} = $cellBorder;
colas@0
   238
            $defaultCssAttrs{cellBorder} = $cellBorder if $writeDefaults;
colas@0
   239
        }
colas@0
   240
    }
colas@0
   241
colas@0
   242
    $tmp = $params{headeralign};
colas@0
   243
    if ( defined $tmp && $tmp ne '' ) {
colas@0
   244
        $tmp =~ s/ //go;    # remove spaces
colas@0
   245
        if ( $tmp ne join( ',', @headerAlign ) ) {
colas@0
   246
            @headerAlign = split( /,/, $tmp );
colas@0
   247
            if (
colas@0
   248
                $useCss
colas@0
   249
                && ( !defined $defaultCssAttrs{'headerAlign'}
colas@0
   250
                    || $tmp ne $defaultCssAttrs{'headerAlign'} )
colas@0
   251
              )
colas@0
   252
            {
colas@0
   253
                $cssAttrs{headerAlign}        = $tmp;    # store string
colas@0
   254
                $defaultCssAttrs{headerAlign} = $tmp
colas@0
   255
                  if $writeDefaults;                     # store string
colas@0
   256
            }
colas@0
   257
        }
colas@0
   258
    }
colas@0
   259
colas@0
   260
    $tmp = $params{dataalign};
colas@0
   261
    if ( defined $tmp && $tmp ne '' ) {
colas@0
   262
        $tmp =~ s/ //go;                                 # remove spaces
colas@0
   263
        if ( $tmp ne join( ',', @dataAlign ) ) {
colas@0
   264
            @dataAlign = split( /,/, $tmp );
colas@0
   265
            if (
colas@0
   266
                $useCss
colas@0
   267
                && ( !defined $defaultCssAttrs{'dataAlign'}
colas@0
   268
                    || $tmp ne $defaultCssAttrs{'dataAlign'} )
colas@0
   269
              )
colas@0
   270
            {
colas@0
   271
                $cssAttrs{dataAlign}        = $tmp;      # store string
colas@0
   272
                $defaultCssAttrs{dataAlign} = $tmp
colas@0
   273
                  if $writeDefaults;                     # store string
colas@0
   274
            }
colas@0
   275
        }
colas@0
   276
    }
colas@0
   277
colas@0
   278
    $tmp = $params{tablewidth};
colas@0
   279
    if ( defined $tmp && $tmp ne '' && $tmp ne $tableWidth ) {
colas@0
   280
        $tableWidth = $tmp;
colas@0
   281
        if (
colas@0
   282
            $useCss
colas@0
   283
            && ( !defined $defaultCssAttrs{'tableWidth'}
colas@0
   284
                || $tmp ne $defaultCssAttrs{'tableWidth'} )
colas@0
   285
          )
colas@0
   286
        {
colas@0
   287
            $cssAttrs{tableWidth} = $tableWidth;
colas@0
   288
            $defaultCssAttrs{tableWidth} = $tableWidth if $writeDefaults;
colas@0
   289
        }
colas@0
   290
    }
colas@0
   291
colas@0
   292
    $tmp = $params{columnwidths};
colas@0
   293
    if ( defined $tmp && $tmp ne '' ) {
colas@0
   294
        $tmp =~ s/ //go;    # remove spaces
colas@0
   295
        if ( $tmp ne join( ',', @columnWidths ) ) {
colas@0
   296
            @columnWidths = split( /,/, $tmp );
colas@0
   297
            if (
colas@0
   298
                $useCss
colas@0
   299
                && ( !defined $defaultCssAttrs{'columnWidths'}
colas@0
   300
                    || $tmp ne $defaultCssAttrs{'columnWidths'} )
colas@0
   301
              )
colas@0
   302
            {
colas@0
   303
                $cssAttrs{columnWidths}        = $tmp;    # store string
colas@0
   304
                $defaultCssAttrs{columnWidths} = $tmp
colas@0
   305
                  if $writeDefaults;                      # store string
colas@0
   306
            }
colas@0
   307
        }
colas@0
   308
    }
colas@0
   309
colas@0
   310
    $tmp = $params{headerrows};
colas@0
   311
    if ( defined $tmp && $tmp ne '' && $tmp ne $headerRows ) {
colas@0
   312
colas@0
   313
        # not used in CSS
colas@0
   314
        $headerRows = $tmp;
colas@0
   315
        $headerRows = 1 if ( $headerRows < 1 );
colas@0
   316
    }
colas@0
   317
colas@0
   318
    $tmp = $params{footerrows};
colas@0
   319
    if ( defined $tmp && $tmp ne '' && $tmp ne $footerRows ) {
colas@0
   320
colas@0
   321
        # not used in CSS
colas@0
   322
        $footerRows = $tmp;
colas@0
   323
    }
colas@0
   324
colas@0
   325
    $tmp = $params{valign};
colas@0
   326
    if ( defined $tmp && $tmp ne '' && $tmp ne $vAlign ) {
colas@0
   327
        $vAlign = $tmp if ( defined $tmp );
colas@0
   328
        if (
colas@0
   329
            $useCss
colas@0
   330
            && ( !defined $defaultCssAttrs{'vAlign'}
colas@0
   331
                || $tmp ne $defaultCssAttrs{'vAlign'} )
colas@0
   332
          )
colas@0
   333
        {
colas@0
   334
            $cssAttrs{vAlign} = $vAlign;
colas@0
   335
            $defaultCssAttrs{vAlign} = $vAlign if $writeDefaults;
colas@0
   336
        }
colas@0
   337
    }
colas@0
   338
colas@0
   339
    $tmp = $params{datavalign};
colas@0
   340
    if ( defined $tmp && $tmp ne '' && $tmp ne $dataVAlign ) {
colas@0
   341
        $dataVAlign = $tmp if ( defined $tmp );
colas@0
   342
        if (
colas@0
   343
            $useCss
colas@0
   344
            && ( !defined $defaultCssAttrs{'dataVAlign'}
colas@0
   345
                || $tmp ne $defaultCssAttrs{'dataVAlign'} )
colas@0
   346
          )
colas@0
   347
        {
colas@0
   348
            $cssAttrs{dataVAlign} = $dataVAlign;
colas@0
   349
            $defaultCssAttrs{dataVAlign} = $dataVAlign if $writeDefaults;
colas@0
   350
        }
colas@0
   351
    }
colas@0
   352
colas@0
   353
    $tmp = $params{headervalign};
colas@0
   354
    if ( defined $tmp && $tmp ne '' && $tmp ne $headerVAlign ) {
colas@0
   355
        $headerVAlign = $tmp if ( defined $tmp );
colas@0
   356
        if (
colas@0
   357
            $useCss
colas@0
   358
            && ( !defined $defaultCssAttrs{'headerVAlign'}
colas@0
   359
                || $tmp ne $defaultCssAttrs{'headerVAlign'} )
colas@0
   360
          )
colas@0
   361
        {
colas@0
   362
            $cssAttrs{headerVAlign} = $headerVAlign;
colas@0
   363
            $defaultCssAttrs{headerVAlign} = $headerVAlign if $writeDefaults;
colas@0
   364
        }
colas@0
   365
    }
colas@0
   366
colas@0
   367
    my $tmpheaderbg = $params{headerbg};
colas@0
   368
    if (   defined $tmpheaderbg
colas@0
   369
        && $tmpheaderbg ne ''
colas@0
   370
        && $tmpheaderbg ne $headerBg )
colas@0
   371
    {
colas@0
   372
        $headerBg = $tmpheaderbg;
colas@0
   373
        if (
colas@0
   374
            $useCss
colas@0
   375
            && ( !defined $defaultCssAttrs{'headerBg'}
colas@0
   376
                || $tmpheaderbg ne $defaultCssAttrs{'headerBg'} )
colas@0
   377
          )
colas@0
   378
        {
colas@0
   379
            $cssAttrs{headerBg} = $headerBg;
colas@0
   380
            $defaultCssAttrs{headerBg} = $headerBg if $writeDefaults;
colas@0
   381
        }
colas@0
   382
    }
colas@0
   383
colas@0
   384
    # only set headerbgsorted color if it is defined
colas@0
   385
    # otherwise use headerbg
colas@0
   386
    my $tmphbgsorted = $tmpheaderbg;
colas@0
   387
    $tmp = $params{headerbgsorted};
colas@0
   388
    if ( defined $tmp && $tmp ne '' ) {
colas@0
   389
        $tmphbgsorted = $tmp;
colas@0
   390
    }
colas@0
   391
colas@0
   392
    if (   defined $tmphbgsorted
colas@0
   393
        && $tmphbgsorted ne ''
colas@0
   394
        && $tmphbgsorted ne $headerBgSorted )
colas@0
   395
    {
colas@0
   396
        $headerBgSorted = $tmphbgsorted;
colas@0
   397
        if (
colas@0
   398
            $useCss
colas@0
   399
            && ( !defined $defaultCssAttrs{'headerBgSorted'}
colas@0
   400
                || $tmphbgsorted ne $defaultCssAttrs{'headerBgSorted'} )
colas@0
   401
          )
colas@0
   402
        {
colas@0
   403
            $cssAttrs{headerBgSorted} = $tmphbgsorted;
colas@0
   404
            $defaultCssAttrs{headerBgSorted} = $tmphbgsorted if $writeDefaults;
colas@0
   405
        }
colas@0
   406
    }
colas@0
   407
colas@0
   408
    $tmp = $params{headercolor};
colas@0
   409
    if ( defined $tmp && $tmp ne '' && $tmp ne $headerColor ) {
colas@0
   410
        $headerColor = $tmp;
colas@0
   411
        if (
colas@0
   412
            $useCss
colas@0
   413
            && ( !defined $defaultCssAttrs{'headerColor'}
colas@0
   414
                || $tmp ne $defaultCssAttrs{'headerColor'} )
colas@0
   415
          )
colas@0
   416
        {
colas@0
   417
            $cssAttrs{headerColor} = $headerColor;
colas@0
   418
            $defaultCssAttrs{headerColor} = $headerColor if $writeDefaults;
colas@0
   419
        }
colas@0
   420
    }
colas@0
   421
colas@0
   422
    my $tmpdatabg = $params{databg};
colas@0
   423
    if ( defined $tmpdatabg && $tmpdatabg ne '' ) {
colas@0
   424
        $tmpdatabg =~ s/ //go;    # remove spaces
colas@0
   425
        if ( $tmpdatabg ne join( ',', @dataBg ) ) {
colas@0
   426
            @dataBg = split( /,/, $tmpdatabg );
colas@0
   427
            if (
colas@0
   428
                $useCss
colas@0
   429
                && ( !defined $defaultCssAttrs{'dataBg'}
colas@0
   430
                    || $tmpdatabg ne $defaultCssAttrs{'dataBg'} )
colas@0
   431
              )
colas@0
   432
            {
colas@0
   433
                $cssAttrs{dataBg}        = $tmpdatabg;    # store string
colas@0
   434
                $defaultCssAttrs{dataBg} = $tmpdatabg
colas@0
   435
                  if $writeDefaults;                      # store string
colas@0
   436
            }
colas@0
   437
        }
colas@0
   438
    }
colas@0
   439
colas@0
   440
    # only set databgsorted color if it is defined
colas@0
   441
    # otherwise use databg
colas@0
   442
    my $tmpdatabgsorted = $tmpdatabg;
colas@0
   443
    $tmp = $params{databgsorted};
colas@0
   444
    if ( defined $tmp && $tmp ne '' ) {
colas@0
   445
        $tmpdatabgsorted = $tmp;
colas@0
   446
    }
colas@0
   447
    if ( defined $tmpdatabgsorted && $tmpdatabgsorted ne '' ) {
colas@0
   448
        $tmpdatabgsorted =~ s/ //go;    # remove spaces
colas@0
   449
        if ( $tmpdatabgsorted ne join( ',', @dataBgSorted ) ) {
colas@0
   450
            @dataBgSorted = split( /,/, $tmpdatabgsorted );
colas@0
   451
            if (
colas@0
   452
                $useCss
colas@0
   453
                && ( !defined $defaultCssAttrs{'dataBgSorted'}
colas@0
   454
                    || $tmpdatabgsorted ne $defaultCssAttrs{'dataBgSorted'} )
colas@0
   455
              )
colas@0
   456
            {
colas@0
   457
                $cssAttrs{dataBgSorted} = $tmpdatabgsorted;    # store string
colas@0
   458
                $defaultCssAttrs{dataBgSorted} = $tmpdatabgsorted
colas@0
   459
                  if $writeDefaults;                           # store string
colas@0
   460
            }
colas@0
   461
        }
colas@0
   462
    }
colas@0
   463
colas@0
   464
    $tmp = $params{datacolor};
colas@0
   465
    if ( defined $tmp && $tmp ne '' ) {
colas@0
   466
        $tmp =~ s/ //go;                                       # remove spaces
colas@0
   467
        if ( $tmp ne join( ',', @dataColor ) ) {
colas@0
   468
            @dataColor = split( /,/, $tmp );
colas@0
   469
            if (
colas@0
   470
                $useCss
colas@0
   471
                && ( !defined $defaultCssAttrs{'dataColor'}
colas@0
   472
                    || $tmp ne $defaultCssAttrs{'dataColor'} )
colas@0
   473
              )
colas@0
   474
            {
colas@0
   475
                $cssAttrs{dataColor}        = $tmp;            # store string
colas@0
   476
                $defaultCssAttrs{dataColor} = $tmp
colas@0
   477
                  if $writeDefaults;                           # store string
colas@0
   478
            }
colas@0
   479
        }
colas@0
   480
    }
colas@0
   481
colas@0
   482
    $tmp = $params{summary};
colas@0
   483
    if ( defined $tmp && $tmp ne '' && $tmp ne $tableSummary ) {
colas@0
   484
        $tableSummary = $tmp;
colas@0
   485
    }
colas@0
   486
colas@0
   487
    $tmp = $params{caption};
colas@0
   488
    if ( defined $tmp && $tmp ne '' && $tmp ne $tableCaption ) {
colas@0
   489
        $tableCaption = $tmp;
colas@0
   490
    }
colas@0
   491
colas@0
   492
    if ($writeDefaults) {
colas@0
   493
colas@0
   494
  # just uncomment to write plugin settings as css styles ( .twikiTable{ ... } )
colas@0
   495
  #_addStylesToHead( $useCss, $writeDefaults, %defaultCssAttrs );
colas@0
   496
    }
colas@0
   497
    else {
colas@0
   498
        _addStylesToHead( $useCss, $writeDefaults, %cssAttrs );
colas@0
   499
    }
colas@0
   500
colas@0
   501
    return $currTablePre . '<nop>';
colas@0
   502
}
colas@0
   503
colas@0
   504
# Convert text to number and date if syntactically possible
colas@0
   505
sub _convertToNumberAndDate {
colas@0
   506
    my ($text) = @_;
colas@0
   507
colas@0
   508
    $text =~ s/&nbsp;/ /go;
colas@0
   509
    $text =~ s/<\/?nobr>/ /go;
colas@0
   510
colas@0
   511
    my $num  = undef;
colas@0
   512
    my $date = undef;
colas@0
   513
    if ( $text =~ /^\s*$/ ) {
colas@0
   514
        $num  = 0;
colas@0
   515
        $date = 0;
colas@0
   516
    }
colas@0
   517
colas@0
   518
    if ( $text =~
colas@0
   519
m|^\s*([0-9]{1,2})[-\s/]*([A-Z][a-z][a-z])[-\s/]*([0-9]{4})\s*-\s*([0-9][0-9]):([0-9][0-9])|
colas@0
   520
      )
colas@0
   521
    {
colas@0
   522
colas@0
   523
        # "31 Dec 2003 - 23:59", "31-Dec-2003 - 23:59",
colas@0
   524
        # "31 Dec 2003 - 23:59 - any suffix"
colas@0
   525
        $date = timegm( 0, $5, $4, $1, $mon2num{$2}, $3 - 1900 );
colas@0
   526
    }
colas@0
   527
    elsif ( $text =~
colas@0
   528
        m|^\s*([0-9]{1,2})[-\s/]([A-Z][a-z][a-z])[-\s/]([0-9]{2,4})\s*$| )
colas@0
   529
    {
colas@0
   530
colas@0
   531
        # "31 Dec 2003", "31 Dec 03", "31-Dec-2003", "31/Dec/2003"
colas@0
   532
        my $year = $3;
colas@0
   533
        $year += 1900 if ( length($year) == 2 && $year > 80 );
colas@0
   534
        $year += 2000 if ( length($year) == 2 );
colas@0
   535
        $date = timegm( 0, 0, 0, $1, $mon2num{$2}, $year - 1900 );
colas@0
   536
    }
colas@0
   537
    elsif ( $text =~ /^\s*[0-9]+(\.[0-9]+)?\s*$/ ) {
colas@0
   538
        $num = $text;
colas@0
   539
    }
colas@0
   540
colas@0
   541
    return ( $num, $date );
colas@0
   542
}
colas@0
   543
colas@0
   544
sub _processTableRow {
colas@0
   545
    my ( $thePre, $theRow ) = @_;
colas@0
   546
colas@0
   547
    $currTablePre = $thePre || '';
colas@0
   548
    my $span = 0;
colas@0
   549
    my $l1   = 0;
colas@0
   550
    my $l2   = 0;
colas@0
   551
colas@0
   552
    if ( !$insideTABLE ) {
colas@0
   553
        @curTable = ();
colas@0
   554
        @rowspan  = ();
colas@0
   555
colas@0
   556
        $tableCount++;
colas@0
   557
        $currentSortDirection = $sortDirection{'NONE'};
colas@0
   558
colas@0
   559
        if (   defined $requestedTable
colas@0
   560
            && $requestedTable == $tableCount
colas@0
   561
            && defined $sortColFromUrl )
colas@0
   562
        {
colas@0
   563
            $sortCol              = $sortColFromUrl;
colas@0
   564
            $sortCol              = $maxSortCols if ( $sortCol > $maxSortCols );
colas@0
   565
            $currentSortDirection = _getCurrentSortDirection($up);
colas@0
   566
        }
colas@0
   567
        elsif ( defined $initSort ) {
colas@0
   568
            $sortCol              = $initSort - 1;
colas@0
   569
            $sortCol              = $maxSortCols if ( $sortCol > $maxSortCols );
colas@0
   570
            $currentSortDirection = _getCurrentSortDirection($initDirection);
colas@0
   571
        }
colas@0
   572
colas@0
   573
    }
colas@0
   574
colas@0
   575
    $theRow =~ s/\t/   /go;    # change tabs to space
colas@0
   576
    $theRow =~ s/\s*$//o;      # remove trailing spaces
colas@0
   577
    $theRow =~ s/(\|\|+)/'colspan'.$translationToken.length($1)."\|"/geo;   # calc COLSPAN
colas@0
   578
    my $colCount = 0;
colas@0
   579
    my @row      = ();
colas@0
   580
    $span = 0;
colas@0
   581
    my $value = '';
colas@0
   582
colas@0
   583
    foreach ( split( /\|/, $theRow ) ) {
colas@0
   584
        my $attr = {};
colas@0
   585
        $span = 1;
colas@0
   586
colas@0
   587
        #AS 25-5-01 Fix to avoid matching also single columns
colas@0
   588
        if (s/colspan$translationToken([0-9]+)//) {
colas@0
   589
            $span = $1;
colas@0
   590
            $attr->{colspan} = $span;
colas@0
   591
        }
colas@0
   592
        s/^\s+$/ &nbsp; /o;
colas@0
   593
        ( $l1, $l2 ) = ( 0, 0 );
colas@0
   594
        if (/^(\s*).*?(\s*)$/) {
colas@0
   595
            $l1 = length($1);
colas@0
   596
            $l2 = length($2);
colas@0
   597
        }
colas@0
   598
        if ( $l1 >= 2 ) {
colas@0
   599
            if ( $l2 <= 1 ) {
colas@0
   600
                $attr->{align} = 'right';
colas@0
   601
            }
colas@0
   602
            else {
colas@0
   603
                $attr->{align} = 'center';
colas@0
   604
            }
colas@0
   605
        }
colas@0
   606
        if ( $span <= 2 ) {
colas@0
   607
            $attr->{class} =
colas@0
   608
              _appendColNumberCssClass( $attr->{class}, $colCount );
colas@0
   609
        }
colas@0
   610
        if (   defined $columnWidths[$colCount]
colas@0
   611
            && $columnWidths[$colCount]
colas@0
   612
            && $span <= 2 )
colas@0
   613
        {
colas@0
   614
colas@0
   615
            # html attribute
colas@0
   616
            $attr->{width} = $columnWidths[$colCount];
colas@0
   617
        }
colas@0
   618
colas@0
   619
        if (/^\s*\^\s*$/) {    # row span above
colas@0
   620
            $rowspan[$colCount]++;
colas@0
   621
            push @row, { text => $value, type => 'Y' };
colas@0
   622
        }
colas@0
   623
        else {
colas@0
   624
            for ( my $col = $colCount ; $col < ( $colCount + $span ) ; $col++ )
colas@0
   625
            {
colas@0
   626
                if ( defined( $rowspan[$col] ) && $rowspan[$col] ) {
colas@0
   627
                    my $nRows = scalar(@curTable);
colas@0
   628
                    my $rspan = $rowspan[$col] + 1;
colas@0
   629
                    if ( $rspan > 1 ) {
colas@0
   630
                        $curTable[ $nRows - $rspan ][$col]->{attrs}->{rowspan} =
colas@0
   631
                          $rspan;
colas@0
   632
                    }
colas@0
   633
                    undef( $rowspan[$col] );
colas@0
   634
                }
colas@0
   635
            }
colas@0
   636
colas@0
   637
            if (
colas@0
   638
                (
colas@0
   639
                    (
colas@0
   640
                        defined $requestedTable
colas@0
   641
                        && $requestedTable == $tableCount
colas@0
   642
                    )
colas@0
   643
                    || defined $initSort
colas@0
   644
                )
colas@0
   645
                && defined $sortCol
colas@0
   646
                && $colCount == $sortCol
colas@0
   647
              )
colas@0
   648
            {
colas@0
   649
colas@0
   650
                # CSS class name
colas@0
   651
                if ( $currentSortDirection == $sortDirection{'ASCENDING'} ) {
colas@0
   652
                    $attr->{class} =
colas@0
   653
                      _appendSortedAscendingCssClass( $attr->{class} );
colas@0
   654
                }
colas@0
   655
                if ( $currentSortDirection == $sortDirection{'DESCENDING'} ) {
colas@0
   656
                    $attr->{class} =
colas@0
   657
                      _appendSortedDescendingCssClass( $attr->{class} );
colas@0
   658
                }
colas@0
   659
            }
colas@0
   660
colas@0
   661
            my $type = '';
colas@0
   662
            if (/^\s*\*(.*)\*\s*$/) {
colas@0
   663
                $value = $1;
colas@0
   664
                if (@headerAlign) {
colas@0
   665
                    my $align =
colas@0
   666
                      @headerAlign[ $colCount % ( $#headerAlign + 1 ) ];
colas@0
   667
colas@0
   668
                    # html attribute
colas@0
   669
                    $attr->{align} = $align;
colas@0
   670
                }
colas@0
   671
                if ($headerVAlign) {
colas@0
   672
colas@0
   673
                    # html attribute
colas@0
   674
                    $attr->{valign} = $headerVAlign if $headerVAlign;
colas@0
   675
                }
colas@0
   676
                elsif ($vAlign) {
colas@0
   677
colas@0
   678
                    # html attribute
colas@0
   679
                    $attr->{valign} = $vAlign;
colas@0
   680
                }
colas@0
   681
                $type = 'th';
colas@0
   682
            }
colas@0
   683
            else {
colas@0
   684
                if (/^\s*(.*?)\s*$/) {    # strip white spaces
colas@0
   685
                    $_ = $1;
colas@0
   686
                }
colas@0
   687
                $value = $_;
colas@0
   688
                if (@dataAlign) {
colas@0
   689
                    my $align = @dataAlign[ $colCount % ( $#dataAlign + 1 ) ];
colas@0
   690
colas@0
   691
                    # html attribute
colas@0
   692
                    $attr->{align} = $align;
colas@0
   693
                }
colas@0
   694
                if ($dataVAlign) {
colas@0
   695
colas@0
   696
                    # html attribute
colas@0
   697
                    $attr->{valign} = $dataVAlign if $dataVAlign;
colas@0
   698
                }
colas@0
   699
                elsif ($vAlign) {
colas@0
   700
colas@0
   701
                    # html attribute
colas@0
   702
                    $attr->{valign} = $vAlign;
colas@0
   703
                }
colas@0
   704
                $type = 'td';
colas@0
   705
            }
colas@0
   706
colas@0
   707
            push @row, { text => $value, attrs => $attr, type => $type };
colas@0
   708
        }
colas@0
   709
        while ( $span > 1 ) {
colas@0
   710
            push @row, { text => $value, type => 'X' };
colas@0
   711
            $colCount++;
colas@0
   712
            $span--;
colas@0
   713
        }
colas@0
   714
        $colCount++;
colas@0
   715
    }
colas@0
   716
    push @curTable, \@row;
colas@0
   717
    return $currTablePre
colas@0
   718
      . '<nop>';    # Avoid TWiki converting empty lines to new paras
colas@0
   719
}
colas@0
   720
colas@0
   721
# Determine whether to generate sorting headers for this table. The header
colas@0
   722
# indicates the context of the table (body or file attachment)
colas@0
   723
sub _shouldISortThisTable {
colas@0
   724
    my ($header) = @_;
colas@0
   725
colas@0
   726
    return 0 unless $sortAllTables;
colas@0
   727
colas@0
   728
    # All cells in header are headings?
colas@0
   729
    foreach my $cell (@$header) {
colas@0
   730
        return 0 if ( $cell->{type} ne 'th' );
colas@0
   731
    }
colas@0
   732
colas@0
   733
    return 1;
colas@0
   734
}
colas@0
   735
colas@0
   736
# Guess if column is a date, number or plain text
colas@0
   737
sub _guessColumnType {
colas@0
   738
    my ($col)         = @_;
colas@0
   739
    my $isDate        = 1;
colas@0
   740
    my $isNum         = 1;
colas@0
   741
    my $num           = '';
colas@0
   742
    my $date          = '';
colas@0
   743
    my $columnIsValid = 0;
colas@0
   744
    foreach my $row (@curTable) {
colas@0
   745
        next if ( !$row->[$col]->{text} );
colas@0
   746
colas@0
   747
        # else
colas@0
   748
        $columnIsValid = 1;
colas@0
   749
        ( $num, $date ) = _convertToNumberAndDate( $row->[$col]->{text} );
colas@0
   750
        $isDate = 0 if ( !defined($date) );
colas@0
   751
        $isNum  = 0 if ( !defined($num) );
colas@0
   752
        last if ( !$isDate && !$isNum );
colas@0
   753
        $row->[$col]->{date}   = $date;
colas@0
   754
        $row->[$col]->{number} = $num;
colas@0
   755
    }
colas@0
   756
    return $columnType{'UNDEFINED'} if ( !$columnIsValid );
colas@0
   757
    my $type = $columnType{'TEXT'};
colas@0
   758
    if ($isDate) {
colas@0
   759
        $type = $columnType{'DATE'};
colas@0
   760
    }
colas@0
   761
    elsif ($isNum) {
colas@0
   762
        $type = $columnType{'NUMBER'};
colas@0
   763
    }
colas@0
   764
    return $type;
colas@0
   765
}
colas@0
   766
colas@0
   767
# Remove HTML from text so it can be sorted
colas@0
   768
sub _stripHtml {
colas@0
   769
    my ($text) = @_;
colas@0
   770
colas@0
   771
    $text =~
colas@0
   772
      s/\[\[[^\]]+\]\[([^\]]+)\]\]/$1/go; # extract label from [[...][...]] link
colas@0
   773
colas@0
   774
    my $orgtext =
colas@0
   775
      $text;    # in case we will remove all contents with stripping html
colas@0
   776
    $text =~ s/<[^>]+>//go;    # strip HTML
colas@0
   777
    $text = _getImageTextForSorting($orgtext) if ( $text eq '' );
colas@0
   778
    $text =~ s/[\[\]\*\|=_\&\<\>]/ /g;    # remove Wiki formatting chars
colas@0
   779
    $text =~ s/^ *//go;                   # strip leading space space
colas@0
   780
    $text = lc($text);                    # convert to lower case
colas@0
   781
    return $text;
colas@0
   782
}
colas@0
   783
colas@0
   784
=pod
colas@0
   785
colas@0
   786
Retrieve text data from an image html tag to be used for sorting.
colas@0
   787
First try the alt tag string. If not available, return the url string.
colas@0
   788
If not available, return the original string.
colas@0
   789
colas@0
   790
=cut
colas@0
   791
colas@0
   792
sub _getImageTextForSorting {
colas@0
   793
    my ($text) = @_;
colas@0
   794
colas@0
   795
    # try to see _if_ there is any img data for sorting
colas@0
   796
    my $hasImageTag = ( $text =~ m/\<\s*img([^>]+)>/ );
colas@0
   797
    return $text if ( !$hasImageTag );
colas@0
   798
colas@0
   799
    # first try to get the alt text
colas@0
   800
    my $key = 'alt';
colas@0
   801
    $text =~ m/$key=\s*[\"\']([^\"\']*)/;
colas@0
   802
    return $1 if ( $1 ne '' );
colas@0
   803
colas@0
   804
    # else
colas@0
   805
colas@0
   806
    # no alt text; use the url
colas@0
   807
    $key = 'url';
colas@0
   808
    $text =~ m/$key=\s*[\"\']([^\"\']*)/;
colas@0
   809
    return $1 if ( $1 ne '' );
colas@0
   810
colas@0
   811
    # else
colas@0
   812
colas@0
   813
    return $text;
colas@0
   814
}
colas@0
   815
colas@0
   816
=pod
colas@0
   817
colas@0
   818
Appends $className to $classList, separated by a space.  
colas@0
   819
colas@0
   820
=cut
colas@0
   821
colas@0
   822
sub _appendToClassList {
colas@0
   823
    my ( $classList, $className ) = @_;
colas@0
   824
    $classList = $classList ? $classList .= ' ' : '';
colas@0
   825
    $classList .= $className;
colas@0
   826
    return $classList;
colas@0
   827
}
colas@0
   828
colas@0
   829
sub _appendSortedCssClass {
colas@0
   830
    my ($classList) = @_;
colas@0
   831
colas@0
   832
    return _appendToClassList( $classList, 'twikiSortedCol' );
colas@0
   833
}
colas@0
   834
colas@0
   835
sub _appendRowNumberCssClass {
colas@0
   836
    my ( $classList, $colListName, $rowNum ) = @_;
colas@0
   837
colas@0
   838
    my $rowClassName = 'twikiTableRow' . $colListName . $rowNum;
colas@0
   839
    return _appendToClassList( $classList, $rowClassName );
colas@0
   840
}
colas@0
   841
colas@0
   842
sub _appendColNumberCssClass {
colas@0
   843
    my ( $classList, $colNum ) = @_;
colas@0
   844
colas@0
   845
    my $colClassName = 'twikiTableCol' . $colNum;
colas@0
   846
    return _appendToClassList( $classList, $colClassName );
colas@0
   847
}
colas@0
   848
colas@0
   849
sub _appendFirstColumnCssClass {
colas@0
   850
    my ($classList) = @_;
colas@0
   851
colas@0
   852
    return _appendToClassList( $classList, 'twikiFirstCol' );
colas@0
   853
}
colas@0
   854
colas@0
   855
sub _appendLastColumnCssClass {
colas@0
   856
    my ($classList) = @_;
colas@0
   857
colas@0
   858
    return _appendToClassList( $classList, 'twikiLastCol' );
colas@0
   859
}
colas@0
   860
colas@0
   861
sub _appendLastRowCssClass {
colas@0
   862
    my ($classList) = @_;
colas@0
   863
colas@0
   864
    return _appendToClassList( $classList, 'twikiLast' );
colas@0
   865
}
colas@0
   866
colas@0
   867
sub _appendSortedAscendingCssClass {
colas@0
   868
    my ($classList) = @_;
colas@0
   869
colas@0
   870
    return _appendToClassList( $classList, 'twikiSortedAscendingCol' );
colas@0
   871
}
colas@0
   872
colas@0
   873
sub _appendSortedDescendingCssClass {
colas@0
   874
    my ($classList) = @_;
colas@0
   875
colas@0
   876
    return _appendToClassList( $classList, 'twikiSortedDescendingCol' );
colas@0
   877
}
colas@0
   878
colas@0
   879
# The default sort direction.
colas@0
   880
sub _getDefaultSortDirection {
colas@0
   881
    return $sortDirection{'ASCENDING'};
colas@0
   882
}
colas@0
   883
colas@0
   884
# Gets the current sort direction.
colas@0
   885
sub _getCurrentSortDirection {
colas@0
   886
    my ($currentDirection) = @_;
colas@0
   887
    $currentDirection ||= _getDefaultSortDirection();
colas@0
   888
    return $currentDirection;
colas@0
   889
}
colas@0
   890
colas@0
   891
# Gets the new sort direction (needed for sort button) based on the current sort
colas@0
   892
# direction.
colas@0
   893
sub _getNewSortDirection {
colas@0
   894
    my ($currentDirection) = @_;
colas@0
   895
    if ( !defined $currentDirection ) {
colas@0
   896
        return _getDefaultSortDirection();
colas@0
   897
    }
colas@0
   898
    my $newDirection;
colas@0
   899
    if ( $currentDirection == $sortDirection{'ASCENDING'} ) {
colas@0
   900
        $newDirection = $sortDirection{'DESCENDING'};
colas@0
   901
    }
colas@0
   902
    if ( $currentDirection == $sortDirection{'DESCENDING'} ) {
colas@0
   903
        if ($unsortEnabled) {
colas@0
   904
            $newDirection = $sortDirection{'NONE'};
colas@0
   905
        }
colas@0
   906
        else {
colas@0
   907
            $newDirection = $sortDirection{'ASCENDING'};
colas@0
   908
        }
colas@0
   909
    }
colas@0
   910
    if ( $currentDirection == $sortDirection{'NONE'} ) {
colas@0
   911
        $newDirection = $sortDirection{'ASCENDING'};
colas@0
   912
    }
colas@0
   913
    return $newDirection;
colas@0
   914
}
colas@0
   915
colas@0
   916
=pod
colas@0
   917
colas@0
   918
Writes css styles to the head if $useCss is true (when custom attributes have been passed to
colas@0
   919
the TABLE{} variable.
colas@0
   920
colas@0
   921
Explicitly set styles override html styling (in this file marked with comment '# html attribute').
colas@0
   922
colas@0
   923
=cut
colas@0
   924
colas@0
   925
sub _addStylesToHead {
colas@0
   926
    my ( $useCss, $writeDefaults, %cssAttrs ) = @_;
colas@0
   927
colas@0
   928
    my @styles = ();
colas@0
   929
colas@0
   930
    if ( !$didWriteDefaultStyle ) {
colas@0
   931
        my $id       = 'default';
colas@0
   932
        my $selector = '.twikiTable';
colas@0
   933
        my $attr     = 'padding-left:.3em; vertical-align:text-bottom;';
colas@0
   934
        push( @styles, ".tableSortIcon img {$attr}" );
colas@0
   935
colas@0
   936
        if ($cellPadding) {
colas@0
   937
            my $attr = 'padding:' . $cellPadding . 'px;';
colas@0
   938
            push( @styles, "$selector td {$attr}" );
colas@0
   939
            push( @styles, "$selector th {$attr}" );
colas@0
   940
        }
colas@0
   941
colas@0
   942
        #_writeStyleToHead( $id, @styles );
colas@0
   943
        $didWriteDefaultStyle = 1;
colas@0
   944
    }
colas@0
   945
colas@0
   946
    # only write default style
colas@0
   947
    return if !$useCss;
colas@0
   948
colas@0
   949
    my $selector = '.twikiTable';
colas@0
   950
    my $id = $writeDefaults ? $writeDefaults : $cssAttrs{tableId};
colas@0
   951
    $selector .= '#' . $id if !$writeDefaults;
colas@0
   952
colas@0
   953
    # tablerules
colas@0
   954
    if ( defined $cssAttrs{tableRules} ) {
colas@0
   955
        if ( $cssAttrs{tableRules} eq 'all' ) {
colas@0
   956
            my $attr = 'border-style:solid;';
colas@0
   957
            push( @styles, "$selector td {$attr}" );
colas@0
   958
            push( @styles, "$selector th {$attr}" );
colas@0
   959
        }
colas@0
   960
        if ( $cssAttrs{tableRules} eq 'none' ) {
colas@0
   961
            my $attr = 'border-style:none;';
colas@0
   962
            push( @styles, "$selector td {$attr}" );
colas@0
   963
            push( @styles, "$selector th {$attr}" );
colas@0
   964
        }
colas@0
   965
        if ( $cssAttrs{tableRules} eq 'cols' ) {
colas@0
   966
            my $attr = 'border-style:none solid;';
colas@0
   967
            push( @styles, "$selector td {$attr}" );
colas@0
   968
            push( @styles, "$selector th {$attr}" );
colas@0
   969
        }
colas@0
   970
        if ( $cssAttrs{tableRules} eq 'rows' ) {
colas@0
   971
            my $attr = 'border-style:solid none;';
colas@0
   972
            push( @styles, "$selector td {$attr}" );
colas@0
   973
            push( @styles, "$selector th {$attr}" );
colas@0
   974
        }
colas@0
   975
        if ( $cssAttrs{tableRules} eq 'groups' ) {
colas@0
   976
            my $attr = 'border-style:solid none;';
colas@0
   977
            push( @styles, "$selector th {$attr}" );
colas@0
   978
            $attr = 'border-style:none;';
colas@0
   979
            push( @styles, "$selector td {$attr}" );
colas@0
   980
        }
colas@0
   981
    }
colas@0
   982
colas@0
   983
    # tableframe
colas@0
   984
    if ( defined $cssAttrs{tableFrame} ) {
colas@0
   985
        my $attr = '';
colas@0
   986
        if ( $cssAttrs{tableFrame} eq 'void' ) {
colas@0
   987
            $attr = 'border-style:none;';
colas@0
   988
        }
colas@0
   989
        if ( $cssAttrs{tableFrame} eq 'above' ) {
colas@0
   990
            $attr = 'border-style:solid none none none;';
colas@0
   991
        }
colas@0
   992
        if ( $cssAttrs{tableFrame} eq 'below' ) {
colas@0
   993
            $attr = 'border-style:none none solid none;';
colas@0
   994
        }
colas@0
   995
        if ( $cssAttrs{tableFrame} eq 'lhs' ) {
colas@0
   996
            $attr = 'border-style:none none none solid;';
colas@0
   997
        }
colas@0
   998
        if ( $cssAttrs{tableFrame} eq 'rhs' ) {
colas@0
   999
            $attr = 'border-style:none solid none none;';
colas@0
  1000
        }
colas@0
  1001
        if ( $cssAttrs{tableFrame} eq 'hsides' ) {
colas@0
  1002
            $attr = 'border-style:solid none solid none;';
colas@0
  1003
        }
colas@0
  1004
        if ( $cssAttrs{tableFrame} eq 'vsides' ) {
colas@0
  1005
            $attr = 'border-style:none solid none solid;';
colas@0
  1006
        }
colas@0
  1007
        if ( $cssAttrs{tableFrame} eq 'box' ) {
colas@0
  1008
            $attr = 'border-style:solid;';
colas@0
  1009
        }
colas@0
  1010
        if ( $cssAttrs{tableFrame} eq 'border' ) {
colas@0
  1011
            $attr = 'border-style:solid;';
colas@0
  1012
        }
colas@0
  1013
        push( @styles, "$selector {$attr}" );
colas@0
  1014
    }
colas@0
  1015
colas@0
  1016
    # tableborder
colas@0
  1017
    if ( defined $cssAttrs{tableBorder} ) {
colas@0
  1018
        my $tableBorderWidth = $cssAttrs{tableBorder} || 0;
colas@0
  1019
        my $attr = 'border-width:' . $tableBorderWidth . 'px;';
colas@0
  1020
        push( @styles, "$selector {$attr}" );
colas@0
  1021
    }
colas@0
  1022
colas@0
  1023
    # cellborder
colas@0
  1024
    if ( defined $cssAttrs{cellBorder} ) {
colas@0
  1025
        my $cellBorderWidth = $cssAttrs{cellBorder} || 0;
colas@0
  1026
        my $attr = 'border-width:' . $cellBorderWidth . 'px;';
colas@0
  1027
        push( @styles, "$selector td {$attr}" );
colas@0
  1028
        push( @styles, "$selector th {$attr}" );
colas@0
  1029
    }
colas@0
  1030
colas@0
  1031
    # tablewidth
colas@0
  1032
    if ( defined $cssAttrs{tableWidth} ) {
colas@0
  1033
        my $attr = 'width:' . $cssAttrs{tableWidth} . ';';
colas@0
  1034
        push( @styles, "$selector {$attr}" );
colas@0
  1035
    }
colas@0
  1036
colas@0
  1037
    # valign
colas@0
  1038
    if ( defined $cssAttrs{vAlign} ) {
colas@0
  1039
        my $attr = 'vertical-align:' . $cssAttrs{vAlign} . ';';
colas@0
  1040
        push( @styles, "$selector td {$attr}" );
colas@0
  1041
        push( @styles, "$selector th {$attr}" );
colas@0
  1042
    }
colas@0
  1043
colas@0
  1044
    # headerVAlign
colas@0
  1045
    if ( defined $cssAttrs{headerVAlign} ) {
colas@0
  1046
        my $attr = 'vertical-align:' . $cssAttrs{headerVAlign} . ';';
colas@0
  1047
        push( @styles, "$selector th {$attr}" );
colas@0
  1048
    }
colas@0
  1049
colas@0
  1050
    # dataVAlign
colas@0
  1051
    if ( defined $cssAttrs{dataVAlign} ) {
colas@0
  1052
        my $attr = 'vertical-align:' . $cssAttrs{dataVAlign} . ';';
colas@0
  1053
        push( @styles, "$selector td {$attr}" );
colas@0
  1054
    }
colas@0
  1055
colas@0
  1056
    # headerbg
colas@0
  1057
    if ( defined $cssAttrs{headerBg} ) {
colas@0
  1058
        unless ( $cssAttrs{headerBg} =~ /none/i ) {
colas@0
  1059
            my $attr = 'background-color:' . $cssAttrs{headerBg} . ';';
colas@0
  1060
            push( @styles, "$selector th {$attr}" );
colas@0
  1061
        }
colas@0
  1062
    }
colas@0
  1063
colas@0
  1064
    # headerbgsorted
colas@0
  1065
    if ( defined $cssAttrs{headerBgSorted} ) {
colas@0
  1066
        unless ( $cssAttrs{headerBgSorted} =~ /none/i ) {
colas@0
  1067
            my $attr = 'background-color:' . $cssAttrs{headerBgSorted} . ';';
colas@0
  1068
            push( @styles, "$selector th.twikiSortedCol {$attr}" );
colas@0
  1069
        }
colas@0
  1070
    }
colas@0
  1071
colas@0
  1072
    # headercolor
colas@0
  1073
    if ( defined $cssAttrs{headerColor} ) {
colas@0
  1074
        my $attr = 'color:' . $cssAttrs{headerColor} . ';';
colas@0
  1075
        push( @styles, "$selector th {$attr}" );
colas@0
  1076
        push( @styles, "$selector th a:link {$attr}" );
colas@0
  1077
        push( @styles, "$selector th a:visited {$attr}" );
colas@0
  1078
        push( @styles, "$selector th a:link font {$attr}" );
colas@0
  1079
        push( @styles, "$selector th a:visited font {$attr}" );
colas@0
  1080
        my $hoverLinkColor = $cssAttrs{headerBg} || '#fff';
colas@0
  1081
        my $hoverBackgroundColor = $cssAttrs{headerColor};
colas@0
  1082
        $attr =
colas@0
  1083
            'color:'
colas@0
  1084
          . $hoverLinkColor
colas@0
  1085
          . ';background-color:'
colas@0
  1086
          . $hoverBackgroundColor . ';';
colas@0
  1087
        push( @styles, "$selector th a:hover {$attr}" );
colas@0
  1088
        push( @styles, "$selector th a:hover font {$attr}" );
colas@0
  1089
    }
colas@0
  1090
colas@0
  1091
    # databg (array)
colas@0
  1092
    if ( defined $cssAttrs{dataBg} ) {
colas@0
  1093
        unless ( $cssAttrs{dataBg} =~ /none/i ) {
colas@0
  1094
            my $count = 0;
colas@0
  1095
            my @attrDataBg = split( /,/, $cssAttrs{dataBg} );
colas@0
  1096
            foreach (@attrDataBg) {
colas@0
  1097
                my $color = $_;
colas@0
  1098
                next if !$color;
colas@0
  1099
                my $rowSelector = 'twikiTableRow' . 'dataBg';
colas@0
  1100
                $rowSelector .= $count;
colas@0
  1101
                my $attr = 'background-color:' . $_ . ';';
colas@0
  1102
                push( @styles, "$selector tr.$rowSelector td {$attr}" );
colas@0
  1103
                $count++;
colas@0
  1104
            }
colas@0
  1105
        }
colas@0
  1106
    }
colas@0
  1107
colas@0
  1108
    # databgsorted (array)
colas@0
  1109
    if ( defined $cssAttrs{dataBgSorted} ) {
colas@0
  1110
        unless ( $cssAttrs{dataBgSorted} =~ /none/i ) {
colas@0
  1111
            my $count = 0;
colas@0
  1112
            my @attrDataBgSorted = split( /,/, $cssAttrs{dataBgSorted} );
colas@0
  1113
            foreach (@attrDataBgSorted) {
colas@0
  1114
                my $color = $_;
colas@0
  1115
                next if !$color;
colas@0
  1116
                my $rowSelector = 'twikiTableRow' . 'dataBg';
colas@0
  1117
                $rowSelector .= $count;
colas@0
  1118
                my $attr = 'background-color:' . $_ . ';';
colas@0
  1119
                push( @styles,
colas@0
  1120
                    "$selector tr.$rowSelector td.twikiSortedCol {$attr}" );
colas@0
  1121
                $count++;
colas@0
  1122
            }
colas@0
  1123
        }
colas@0
  1124
    }
colas@0
  1125
colas@0
  1126
    # datacolor (array)
colas@0
  1127
    if ( defined $cssAttrs{dataColor} ) {
colas@0
  1128
        unless ( $cssAttrs{dataColor} =~ /none/i ) {
colas@0
  1129
            my $count = 0;
colas@0
  1130
            my @attrDataColor = split( /,/, $cssAttrs{dataColor} );
colas@0
  1131
            foreach (@attrDataColor) {
colas@0
  1132
                my $color = $_;
colas@0
  1133
                next if !$color;
colas@0
  1134
                my $rowSelector = 'twikiTableRow' . 'dataColor';
colas@0
  1135
                $rowSelector .= $count;
colas@0
  1136
                my $attr = 'color:' . $_ . ';';
colas@0
  1137
                push( @styles, "$selector tr.$rowSelector td {$attr}" );
colas@0
  1138
                push( @styles, "$selector tr.$rowSelector td font {$attr}" );
colas@0
  1139
                $count++;
colas@0
  1140
            }
colas@0
  1141
        }
colas@0
  1142
    }
colas@0
  1143
colas@0
  1144
    # columnwidths
colas@0
  1145
    if ( defined $cssAttrs{columnWidths} ) {
colas@0
  1146
        my $count = 0;
colas@0
  1147
        my @attrColumnWidths = split( /,/, $cssAttrs{columnWidths} );
colas@0
  1148
        foreach (@attrColumnWidths) {
colas@0
  1149
            my $width = $_;
colas@0
  1150
            next if !$width;
colas@0
  1151
            my $colSelector = 'twikiTableCol';
colas@0
  1152
            $colSelector .= $count;
colas@0
  1153
            my $attr = 'width:' . $_ . ';';
colas@0
  1154
            push( @styles, "$selector td.$colSelector {$attr}" );
colas@0
  1155
            push( @styles, "$selector th.$colSelector {$attr}" );
colas@0
  1156
            $count++;
colas@0
  1157
        }
colas@0
  1158
    }
colas@0
  1159
colas@0
  1160
    # headeralign
colas@0
  1161
    if ( defined $cssAttrs{headerAlign} ) {
colas@0
  1162
        my @attrHeaderAlign = split( /,/, $cssAttrs{headerAlign} );
colas@0
  1163
        if ( scalar @attrHeaderAlign == 1 ) {
colas@0
  1164
            my $align = $attrHeaderAlign[0];
colas@0
  1165
            my $attr  = 'text-align:' . $align . ';';
colas@0
  1166
            push( @styles, "$selector th {$attr}" );
colas@0
  1167
        }
colas@0
  1168
        else {
colas@0
  1169
            my $count = 0;
colas@0
  1170
            foreach (@attrHeaderAlign) {
colas@0
  1171
                my $width = $_;
colas@0
  1172
                next if !$width;
colas@0
  1173
                my $colSelector = 'twikiTableCol';
colas@0
  1174
                $colSelector .= $count;
colas@0
  1175
                my $attr = 'text-align:' . $_ . ';';
colas@0
  1176
                push( @styles, "$selector th.$colSelector {$attr}" );
colas@0
  1177
                $count++;
colas@0
  1178
            }
colas@0
  1179
        }
colas@0
  1180
    }
colas@0
  1181
colas@0
  1182
    # dataAlign
colas@0
  1183
    if ( defined $cssAttrs{dataAlign} ) {
colas@0
  1184
        my @attrDataAlign = split( /,/, $cssAttrs{dataAlign} );
colas@0
  1185
        if ( scalar @attrDataAlign == 1 ) {
colas@0
  1186
            my $align = $attrDataAlign[0];
colas@0
  1187
            my $attr  = 'text-align:' . $align . ';';
colas@0
  1188
            push( @styles, "$selector td {$attr}" );
colas@0
  1189
        }
colas@0
  1190
        else {
colas@0
  1191
            my $count = 0;
colas@0
  1192
            foreach (@attrDataAlign) {
colas@0
  1193
                my $width = $_;
colas@0
  1194
                next if !$width;
colas@0
  1195
                my $colSelector = 'twikiTableCol';
colas@0
  1196
                $colSelector .= $count;
colas@0
  1197
                my $attr = 'text-align:' . $_ . ';';
colas@0
  1198
                push( @styles, "$selector td.$colSelector {$attr}" );
colas@0
  1199
                $count++;
colas@0
  1200
            }
colas@0
  1201
        }
colas@0
  1202
    }
colas@0
  1203
colas@0
  1204
    # cellspacing : no good css equivalent; use table tag attribute
colas@0
  1205
colas@0
  1206
    # cellpadding
colas@0
  1207
    if ( defined $cssAttrs{cellPadding} ) {
colas@0
  1208
        my $attr = 'padding:' . $cssAttrs{cellPadding} . 'px;';
colas@0
  1209
        push( @styles, "$selector td {$attr}" );
colas@0
  1210
        push( @styles, "$selector th {$attr}" );
colas@0
  1211
    }
colas@0
  1212
colas@0
  1213
    return if !scalar @styles;
colas@0
  1214
    _writeStyleToHead( $id, @styles );
colas@0
  1215
}
colas@0
  1216
colas@0
  1217
sub _writeStyleToHead {
colas@0
  1218
    my ( $id, @styles ) = @_;
colas@0
  1219
colas@0
  1220
    my $style = join( "\n", @styles );
colas@0
  1221
    my $header =
colas@0
  1222
      '<style type="text/css" media="all">' . "\n" . $style . "\n" . '</style>';
colas@0
  1223
    TWiki::Func::addToHEAD( 'TABLEPLUGIN_' . $id, $header );
colas@0
  1224
}
colas@0
  1225
colas@0
  1226
sub emitTable {
colas@0
  1227
colas@0
  1228
    #Validate headerrows/footerrows and modify if out of range
colas@0
  1229
    if ( $headerRows > @curTable ) {
colas@0
  1230
        $headerRows = @curTable;    # limit header to size of table!
colas@0
  1231
    }
colas@0
  1232
    if ( $headerRows + $footerRows > @curTable ) {
colas@0
  1233
        $footerRows = @curTable - $headerRows;  # and footer to whatever is left
colas@0
  1234
    }
colas@0
  1235
colas@0
  1236
    my $sortThisTable = _shouldISortThisTable( $curTable[ $headerRows - 1 ] );
colas@0
  1237
    my $tattrs = { class => 'twikiTable' };
colas@0
  1238
    $tattrs->{border} = $tableBorder
colas@0
  1239
      if defined $tableBorder && $tableBorder ne '';
colas@0
  1240
    $tattrs->{cellspacing} = $cellSpacing
colas@0
  1241
      if defined $cellSpacing && $cellSpacing ne '';
colas@0
  1242
    $tattrs->{cellpadding} = $cellPadding
colas@0
  1243
      if defined $cellPadding && $cellPadding ne '';
colas@0
  1244
    $tattrs->{id} = $tableId if defined $tableId && $tableId ne '';
colas@0
  1245
    $tattrs->{summary} = $tableSummary
colas@0
  1246
      if defined $tableSummary && $tableSummary ne '';
colas@0
  1247
    $tattrs->{frame} = $tableFrame if defined $tableFrame && $tableFrame ne '';
colas@0
  1248
    $tattrs->{rules} = $tableRules if defined $tableRules && $tableRules ne '';
colas@0
  1249
    $tattrs->{width} = $tableWidth if defined $tableWidth && $tableWidth ne '';
colas@0
  1250
colas@0
  1251
    my $text = $currTablePre . CGI::start_table($tattrs);
colas@0
  1252
    $text .= $currTablePre . CGI::caption($tableCaption) if ($tableCaption);
colas@0
  1253
    my $stype = '';
colas@0
  1254
colas@0
  1255
    # count the number of cols to prevent looping over non-existing columns
colas@0
  1256
    my $maxCols = 0;
colas@0
  1257
colas@0
  1258
    #Flush out any remaining rowspans
colas@0
  1259
    for ( my $i = 0 ; $i < @rowspan ; $i++ ) {
colas@0
  1260
        if ( defined( $rowspan[$i] ) && $rowspan[$i] ) {
colas@0
  1261
            my $nRows = scalar(@curTable);
colas@0
  1262
            my $rspan = $rowspan[$i] + 1;
colas@0
  1263
            my $r     = $nRows - $rspan;
colas@0
  1264
            $curTable[$r][$i]->{attrs} ||= {};
colas@0
  1265
            if ( $rspan > 1 ) {
colas@0
  1266
                $curTable[$r][$i]->{attrs}->{rowspan} = $rspan;
colas@0
  1267
            }
colas@0
  1268
        }
colas@0
  1269
    }
colas@0
  1270
colas@0
  1271
    if (
colas@0
  1272
        (
colas@0
  1273
               defined $sortCol
colas@0
  1274
            && defined $requestedTable
colas@0
  1275
            && $requestedTable == $tableCount
colas@0
  1276
        )
colas@0
  1277
        || defined $initSort
colas@0
  1278
      )
colas@0
  1279
    {
colas@0
  1280
colas@0
  1281
        # DG 08 Aug 2002: Allow multi-line headers
colas@0
  1282
        my @header = splice( @curTable, 0, $headerRows );
colas@0
  1283
colas@0
  1284
        # DG 08 Aug 2002: Skip sorting any trailers as well
colas@0
  1285
        my @trailer = ();
colas@0
  1286
        if ( $footerRows && scalar(@curTable) > $footerRows ) {
colas@0
  1287
            @trailer = splice( @curTable, -$footerRows );
colas@0
  1288
        }
colas@0
  1289
colas@0
  1290
        # Count the maximum number of columns of this table
colas@0
  1291
        for my $row ( 0 .. $#curTable ) {
colas@0
  1292
            my $thisRowMaxColCount = 0;
colas@0
  1293
            for my $col ( 0 .. $#{ $curTable[$row] } ) {
colas@0
  1294
                $thisRowMaxColCount++;
colas@0
  1295
            }
colas@0
  1296
            $maxCols = $thisRowMaxColCount
colas@0
  1297
              if ( $thisRowMaxColCount > $maxCols );
colas@0
  1298
        }
colas@0
  1299
colas@0
  1300
        # Handle multi-row labels by killing rowspans in sorted tables
colas@0
  1301
        for my $row ( 0 .. $#curTable ) {
colas@0
  1302
            for my $col ( 0 .. $#{ $curTable[$row] } ) {
colas@0
  1303
                $curTable[$row][$col]->{attrs}->{rowspan} = 1;
colas@0
  1304
                if ( $curTable[$row][$col]->{type} eq 'Y' ) {
colas@0
  1305
                    $curTable[$row][$col]->{text} =
colas@0
  1306
                      $curTable[ $row - 1 ][$col]->{text};
colas@0
  1307
                    $curTable[$row][$col]->{type} = 'td';
colas@0
  1308
                }
colas@0
  1309
            }
colas@0
  1310
        }
colas@0
  1311
colas@0
  1312
        $stype = $columnType{'UNDEFINED'};
colas@0
  1313
colas@0
  1314
        # only get the column type if within bounds
colas@0
  1315
        if ( $sortCol < $maxCols ) {
colas@0
  1316
            $stype = _guessColumnType($sortCol);
colas@0
  1317
        }
colas@0
  1318
colas@0
  1319
        # invalidate sorting if no valid column
colas@0
  1320
        if ( $stype eq $columnType{'UNDEFINED'} ) {
colas@0
  1321
            undef $initSort;
colas@0
  1322
            undef $sortCol;
colas@0
  1323
        }
colas@0
  1324
        elsif ( $stype eq $columnType{'TEXT'} ) {
colas@0
  1325
            if ( $currentSortDirection == $sortDirection{'DESCENDING'} ) {
colas@0
  1326
colas@0
  1327
                # efficient way of sorting stripped HTML text
colas@0
  1328
                # SMELL: efficient? That's not efficient!
colas@0
  1329
                @curTable = map { $_->[0] }
colas@0
  1330
                  sort { $b->[1] cmp $a->[1] }
colas@0
  1331
                  map { [ $_, _stripHtml( $_->[$sortCol]->{text} ) ] }
colas@0
  1332
                  @curTable;
colas@0
  1333
            }
colas@0
  1334
            if ( $currentSortDirection == $sortDirection{'ASCENDING'} ) {
colas@0
  1335
                @curTable = map { $_->[0] }
colas@0
  1336
                  sort { $a->[1] cmp $b->[1] }
colas@0
  1337
                  map { [ $_, _stripHtml( $_->[$sortCol]->{text} ) ] }
colas@0
  1338
                  @curTable;
colas@0
  1339
            }
colas@0
  1340
        }
colas@0
  1341
        else {
colas@0
  1342
            if ( $currentSortDirection == $sortDirection{'DESCENDING'} ) {
colas@0
  1343
                @curTable =
colas@0
  1344
                  sort { $b->[$sortCol]->{$stype} <=> $a->[$sortCol]->{$stype} }
colas@0
  1345
                  @curTable;
colas@0
  1346
            }
colas@0
  1347
            if ( $currentSortDirection == $sortDirection{'ASCENDING'} ) {
colas@0
  1348
                @curTable =
colas@0
  1349
                  sort { $a->[$sortCol]->{$stype} <=> $b->[$sortCol]->{$stype} }
colas@0
  1350
                  @curTable;
colas@0
  1351
            }
colas@0
  1352
colas@0
  1353
        }
colas@0
  1354
colas@0
  1355
        # DG 08 Aug 2002: Cleanup after the header/trailer splicing
colas@0
  1356
        # this is probably awfully inefficient - but how big is a table?
colas@0
  1357
        @curTable = ( @header, @curTable, @trailer );
colas@0
  1358
    }    # if defined $sortCol ...
colas@0
  1359
colas@0
  1360
    my $rowCount       = 0;
colas@0
  1361
    my $numberOfRows   = scalar(@curTable);
colas@0
  1362
    my $dataColorCount = 0;
colas@0
  1363
colas@0
  1364
    my @headerRowList = ();
colas@0
  1365
    my @bodyRowList   = ();
colas@0
  1366
    my @footerRowList = ();
colas@0
  1367
colas@0
  1368
    my $isPastHeaderRows = 0;
colas@0
  1369
    my $singleIndent     = "\n\t";
colas@0
  1370
    my $doubleIndent     = "\n\t\t";
colas@0
  1371
    my $tripleIndent     = "\n\t\t\t";
colas@0
  1372
colas@0
  1373
    foreach my $row (@curTable) {
colas@0
  1374
        my $rowtext  = '';
colas@0
  1375
        my $colCount = 0;
colas@0
  1376
colas@0
  1377
        # keep track of header cells: if all cells are header cells, do not
colas@0
  1378
        # update the data color count
colas@0
  1379
        my $headerCellCount = 0;
colas@0
  1380
        my $numberOfCols    = scalar(@$row);
colas@0
  1381
colas@0
  1382
        foreach my $fcell (@$row) {
colas@0
  1383
colas@0
  1384
            # check if cell exists
colas@0
  1385
            next if ( !$fcell || !$fcell->{type} );
colas@0
  1386
colas@0
  1387
            my $tableAnchor = '';
colas@0
  1388
            next
colas@0
  1389
              if ( $fcell->{type} eq 'X' )
colas@0
  1390
              ;    # data was there so sort could work with col spanning
colas@0
  1391
            my $type = $fcell->{type};
colas@0
  1392
            my $cell = $fcell->{text};
colas@0
  1393
            my $attr = $fcell->{attrs} || {};
colas@0
  1394
colas@0
  1395
            my $newDirection;
colas@0
  1396
            my $isSorted = 0;
colas@0
  1397
colas@0
  1398
            if (
colas@0
  1399
                   $currentSortDirection != $sortDirection{'NONE'}
colas@0
  1400
                && defined $sortCol
colas@0
  1401
                && $colCount == $sortCol
colas@0
  1402
colas@0
  1403
                # Removing the line below hides the marking of sorted columns
colas@0
  1404
                # until the user clicks on a header (KJL)
colas@0
  1405
                # && defined $requestedTable && $requestedTable == $tableCount
colas@0
  1406
                && $stype ne ''
colas@0
  1407
              )
colas@0
  1408
            {
colas@0
  1409
                $isSorted     = 1;
colas@0
  1410
                $newDirection = _getNewSortDirection($currentSortDirection);
colas@0
  1411
            }
colas@0
  1412
            else {
colas@0
  1413
                $newDirection = _getDefaultSortDirection();
colas@0
  1414
            }
colas@0
  1415
colas@0
  1416
            if ( $type eq 'th' ) {
colas@0
  1417
                $headerCellCount++;
colas@0
  1418
                unless ($upchar) {
colas@0
  1419
                    $upchar = CGI::span(
colas@0
  1420
                        { class => 'tableSortIcon tableSortUp' },
colas@0
  1421
                        CGI::img(
colas@0
  1422
                            {
colas@0
  1423
                                src    => $iconUrl . 'tablesortup.gif',
colas@0
  1424
                                border => 0,
colas@0
  1425
                                width  => 11,
colas@0
  1426
                                height => 13,
colas@0
  1427
                                alt    => 'Sorted ascending',
colas@0
  1428
                                title  => 'Sorted ascending'
colas@0
  1429
                            }
colas@0
  1430
                        )
colas@0
  1431
                    );
colas@0
  1432
                    $downchar = CGI::span(
colas@0
  1433
                        { class => 'tableSortIcon tableSortDown' },
colas@0
  1434
                        CGI::img(
colas@0
  1435
                            {
colas@0
  1436
                                src    => $iconUrl . 'tablesortdown.gif',
colas@0
  1437
                                border => 0,
colas@0
  1438
                                width  => 11,
colas@0
  1439
                                height => 13,
colas@0
  1440
                                alt    => 'Sorted descending',
colas@0
  1441
                                title  => 'Sorted descending'
colas@0
  1442
                            }
colas@0
  1443
                        )
colas@0
  1444
                    );
colas@0
  1445
                    $diamondchar = CGI::span(
colas@0
  1446
                        { class => 'tableSortIcon tableSortUp' },
colas@0
  1447
                        CGI::img(
colas@0
  1448
                            {
colas@0
  1449
                                src    => $iconUrl . 'tablesortdiamond.gif',
colas@0
  1450
                                border => 0,
colas@0
  1451
                                width  => 11,
colas@0
  1452
                                height => 13,
colas@0
  1453
                                alt    => 'Sort',
colas@0
  1454
                                title  => 'Sort'
colas@0
  1455
                            }
colas@0
  1456
                        )
colas@0
  1457
                    );
colas@0
  1458
                }
colas@0
  1459
colas@0
  1460
                # DG: allow headers without b.g too (consistent and yes,
colas@0
  1461
                # I use this)
colas@0
  1462
                # html attribute
colas@0
  1463
                $attr->{bgcolor} = $headerBg unless ( $headerBg =~ /none/i );
colas@0
  1464
colas@0
  1465
                # attribute 'maxcols' does not exist in html
colas@0
  1466
                # so commenting out
colas@0
  1467
                #$attr->{maxCols} = $maxCols;
colas@0
  1468
colas@0
  1469
                if ($isSorted) {
colas@0
  1470
                    if ( $currentSortDirection == $sortDirection{'ASCENDING'} )
colas@0
  1471
                    {
colas@0
  1472
                        $tableAnchor = $upchar;
colas@0
  1473
                    }
colas@0
  1474
                    if ( $currentSortDirection == $sortDirection{'DESCENDING'} )
colas@0
  1475
                    {
colas@0
  1476
                        $tableAnchor = $downchar;
colas@0
  1477
                    }
colas@0
  1478
                }
colas@0
  1479
colas@0
  1480
                if (   defined $sortCol
colas@0
  1481
                    && $colCount == $sortCol
colas@0
  1482
                    && defined $requestedTable
colas@0
  1483
                    && $requestedTable == $tableCount )
colas@0
  1484
                {
colas@0
  1485
colas@0
  1486
                    $tableAnchor =
colas@0
  1487
                      CGI::a( { name => 'sorted_table' }, '<!-- -->' )
colas@0
  1488
                      . $tableAnchor;
colas@0
  1489
                }
colas@0
  1490
colas@0
  1491
                if ($headerColor) {
colas@0
  1492
colas@0
  1493
                    my $cellAttrs = { color => $headerColor };
colas@0
  1494
colas@0
  1495
                    # html attribute
colas@0
  1496
                    $cell = CGI::font( $cellAttrs, $cell );
colas@0
  1497
                }
colas@0
  1498
colas@0
  1499
                if ( $sortThisTable && $rowCount == $headerRows - 1 ) {
colas@0
  1500
                    if ($isSorted) {
colas@0
  1501
                        unless ( $headerBgSorted =~ /none/i ) {
colas@0
  1502
colas@0
  1503
                            # html attribute
colas@0
  1504
                            $attr->{bgcolor} = $headerBgSorted;
colas@0
  1505
                        }
colas@0
  1506
                    }
colas@0
  1507
colas@0
  1508
                    my $debugText      = '';
colas@0
  1509
                    my $linkAttributes = {
colas@0
  1510
                        href => $url
colas@0
  1511
                          . 'sortcol='
colas@0
  1512
                          . $colCount
colas@0
  1513
                          . ';table='
colas@0
  1514
                          . $tableCount . ';up='
colas@0
  1515
                          . $newDirection
colas@0
  1516
                          . '#sorted_table',
colas@0
  1517
                        rel   => 'nofollow',
colas@0
  1518
                        title => 'Sort by this column'
colas@0
  1519
                    };
colas@0
  1520
                    if ( $cell =~ /\[\[|href/o ) {
colas@0
  1521
                        $cell .=
colas@0
  1522
                            $debugText . ' '
colas@0
  1523
                          . CGI::a( $linkAttributes, $diamondchar )
colas@0
  1524
                          . $tableAnchor;
colas@0
  1525
                    }
colas@0
  1526
                    else {
colas@0
  1527
                        $cell =
colas@0
  1528
                            $debugText
colas@0
  1529
                          . CGI::a( $linkAttributes, $cell )
colas@0
  1530
                          . $tableAnchor;
colas@0
  1531
                    }
colas@0
  1532
                }
colas@0
  1533
colas@0
  1534
            }
colas@0
  1535
            else {
colas@0
  1536
colas@0
  1537
                # $type is not 'th'
colas@0
  1538
                if (@dataBg) {
colas@0
  1539
                    my $bgcolor;
colas@0
  1540
                    if ( $isSorted && @dataBgSorted ) {
colas@0
  1541
                        $bgcolor =
colas@0
  1542
                          $dataBgSorted[ $dataColorCount % (
colas@0
  1543
                              $#dataBgSorted + 1 ) ];
colas@0
  1544
                    }
colas@0
  1545
                    else {
colas@0
  1546
                        $bgcolor =
colas@0
  1547
                          $dataBg[ $dataColorCount % ( $#dataBg + 1 ) ];
colas@0
  1548
                    }
colas@0
  1549
                    unless ( $bgcolor =~ /none/i ) {
colas@0
  1550
colas@0
  1551
                        # html attribute
colas@0
  1552
                        $attr->{bgcolor} = $bgcolor;
colas@0
  1553
                    }
colas@0
  1554
                }
colas@0
  1555
                if (@dataColor) {
colas@0
  1556
                    my $color =
colas@0
  1557
                      $dataColor[ $dataColorCount % ( $#dataColor + 1 ) ];
colas@0
  1558
colas@0
  1559
                    unless ( $color =~ /^(none)$/i ) {
colas@0
  1560
                        my $cellAttrs = { color => $color };
colas@0
  1561
colas@0
  1562
                        # html attribute
colas@0
  1563
                        $cell = CGI::font( $cellAttrs, ' ' . $cell . ' ' );
colas@0
  1564
                    }
colas@0
  1565
                }
colas@0
  1566
                $type = 'td' unless $type eq 'Y';
colas@0
  1567
            }    ###if( $type eq 'th' )
colas@0
  1568
colas@0
  1569
            if ($isSorted) {
colas@0
  1570
                $attr->{class} = _appendSortedCssClass( $attr->{class} );
colas@0
  1571
            }
colas@0
  1572
colas@0
  1573
            my $isLastRow = ( $rowCount == $numberOfRows - 1 );
colas@0
  1574
            if ( $attr->{rowspan} ) {
colas@0
  1575
                $isLastRow =
colas@0
  1576
                  ( ( $rowCount + ( $attr->{rowspan} - 1 ) ) ==
colas@0
  1577
                      $numberOfRows - 1 );
colas@0
  1578
            }
colas@0
  1579
colas@0
  1580
            # CSS class name
colas@0
  1581
            $attr->{class} = _appendFirstColumnCssClass( $attr->{class} )
colas@0
  1582
              if $colCount == 0;
colas@0
  1583
            my $isLastCol = ( $colCount == $numberOfCols - 1 );
colas@0
  1584
            $attr->{class} = _appendLastColumnCssClass( $attr->{class} )
colas@0
  1585
              if $isLastCol;
colas@0
  1586
colas@0
  1587
            $attr->{class} = _appendLastRowCssClass( $attr->{class} )
colas@0
  1588
              if $isLastRow;
colas@0
  1589
colas@0
  1590
            $colCount++;
colas@0
  1591
            next if ( $type eq 'Y' );
colas@0
  1592
            my $fn = 'CGI::' . $type;
colas@0
  1593
            no strict 'refs';
colas@0
  1594
            $rowtext .= "$tripleIndent" . &$fn( $attr, " $cell " );
colas@0
  1595
            use strict 'refs';
colas@0
  1596
        }    # foreach my $fcell ( @$row )
colas@0
  1597
colas@0
  1598
        # assign css class names to tr
colas@0
  1599
        # based on settings: dataBg, dataBgSorted
colas@0
  1600
        my $trClassName = '';
colas@0
  1601
colas@0
  1602
        # just 2 css names is too limited, but we will keep it for compatibility
colas@0
  1603
        # with existing style sheets
colas@0
  1604
        my $rowTypeName =
colas@0
  1605
          ( $rowCount % 2 ) ? 'twikiTableEven' : 'twikiTableOdd';
colas@0
  1606
        $trClassName = _appendToClassList( $trClassName, $rowTypeName );
colas@0
  1607
colas@0
  1608
        if ( scalar @dataBgSorted ) {
colas@0
  1609
            my $modRowNum = $dataColorCount % ( $#dataBgSorted + 1 );
colas@0
  1610
            $trClassName =
colas@0
  1611
              _appendRowNumberCssClass( $trClassName, 'dataBgSorted',
colas@0
  1612
                $modRowNum );
colas@0
  1613
        }
colas@0
  1614
        if ( scalar @dataBg ) {
colas@0
  1615
            my $modRowNum = $dataColorCount % ( $#dataBg + 1 );
colas@0
  1616
            $trClassName =
colas@0
  1617
              _appendRowNumberCssClass( $trClassName, 'dataBg', $modRowNum );
colas@0
  1618
        }
colas@0
  1619
        if ( scalar @dataColor ) {
colas@0
  1620
            my $modRowNum = $dataColorCount % ( $#dataColor + 1 );
colas@0
  1621
            $trClassName =
colas@0
  1622
              _appendRowNumberCssClass( $trClassName, 'dataColor', $modRowNum );
colas@0
  1623
        }
colas@0
  1624
        $rowtext .= $doubleIndent;
colas@0
  1625
        my $rowHTML =
colas@0
  1626
          $doubleIndent . CGI::Tr( { class => $trClassName }, $rowtext );
colas@0
  1627
colas@0
  1628
        my $isHeaderRow = ( $headerCellCount == $colCount );
colas@0
  1629
        my $isFooterRow = ( ( $numberOfRows - $rowCount ) <= $footerRows );
colas@0
  1630
colas@0
  1631
		if (!$isHeaderRow && !$isFooterRow) {
colas@0
  1632
			# don't include non-adjacent header rows to the top block of header rows
colas@0
  1633
			$isPastHeaderRows = 1;
colas@0
  1634
		}
colas@0
  1635
		
colas@0
  1636
		
colas@0
  1637
        if ($isFooterRow) {
colas@0
  1638
            push @footerRowList, $rowHTML;
colas@0
  1639
        }
colas@0
  1640
        elsif ($isHeaderRow && !$isPastHeaderRows) {
colas@0
  1641
            push( @headerRowList, $rowHTML );
colas@0
  1642
        }
colas@0
  1643
        else {
colas@0
  1644
            push @bodyRowList, $rowHTML;
colas@0
  1645
            $dataColorCount++;
colas@0
  1646
        }
colas@0
  1647
        
colas@0
  1648
		if ($isHeaderRow) {
colas@0
  1649
			# reset data color count to start with first color after
colas@0
  1650
            # each table heading
colas@0
  1651
            $dataColorCount = 0;
colas@0
  1652
		}
colas@0
  1653
		
colas@0
  1654
        $rowCount++;
colas@0
  1655
    }    # foreach my $row ( @curTable )
colas@0
  1656
colas@0
  1657
    my $thead =
colas@0
  1658
        "$singleIndent<thead>"
colas@0
  1659
      . join( "", @headerRowList )
colas@0
  1660
      . "$singleIndent</thead>";
colas@0
  1661
    $text .= $currTablePre . $thead if scalar @headerRowList;
colas@0
  1662
colas@0
  1663
    my $tfoot =
colas@0
  1664
        "$singleIndent<tfoot>"
colas@0
  1665
      . join( "", @footerRowList )
colas@0
  1666
      . "$singleIndent</tfoot>";
colas@0
  1667
    $text .= $currTablePre . $tfoot if scalar @footerRowList;
colas@0
  1668
colas@0
  1669
    my $tbody =
colas@0
  1670
      "$singleIndent<tbody>" . join( "", @bodyRowList ) . "$singleIndent</tbody>";
colas@0
  1671
    $text .= $currTablePre . $tbody if scalar @bodyRowList;
colas@0
  1672
colas@0
  1673
    $text .= $currTablePre . CGI::end_table() . "\n";
colas@0
  1674
    _setDefaults();
colas@0
  1675
    return $text;
colas@0
  1676
}
colas@0
  1677
colas@0
  1678
sub handler {
colas@0
  1679
    ### my ( $text, $removed ) = @_;
colas@0
  1680
colas@0
  1681
    unless ($TWiki::Plugins::TablePlugin::initialised) {
colas@0
  1682
        $insideTABLE = 0;
colas@0
  1683
        $tableCount  = 0;
colas@0
  1684
colas@0
  1685
        $twoCol = 1;
colas@0
  1686
colas@0
  1687
        my $cgi = TWiki::Func::getCgiQuery();
colas@0
  1688
        return unless $cgi;
colas@0
  1689
colas@0
  1690
        # Extract and attach existing parameters
colas@0
  1691
        my $plist = $cgi->query_string();
colas@0
  1692
        $plist =~ s/\;/\&/go;
colas@0
  1693
        $plist =~ s/\&?sortcol.*up=[0-9]+\&?//go;
colas@0
  1694
        $plist .= '&' if $plist;
colas@0
  1695
        $url = $cgi->url . $cgi->path_info() . '?' . $plist;
colas@0
  1696
        $url =~ s/\&/\&amp;/go;
colas@0
  1697
        $sortColFromUrl =
colas@0
  1698
          $cgi->param('sortcol');    # zero based: 0 is first column
colas@0
  1699
        $requestedTable = $cgi->param('table');
colas@0
  1700
        $up             = $cgi->param('up');
colas@0
  1701
colas@0
  1702
        $sortTablesInText = 0;
colas@0
  1703
        $sortAttachments  = 0;
colas@0
  1704
        my $tmp = TWiki::Func::getPreferencesValue('TABLEPLUGIN_SORT');
colas@0
  1705
        if ( !$tmp || $tmp =~ /^all$/oi ) {
colas@0
  1706
            $sortTablesInText = 1;
colas@0
  1707
            $sortAttachments  = 1;
colas@0
  1708
        }
colas@0
  1709
        elsif ( $tmp =~ /^attachments$/oi ) {
colas@0
  1710
            $sortAttachments = 1;
colas@0
  1711
        }
colas@0
  1712
colas@0
  1713
        $pluginAttrs =
colas@0
  1714
          TWiki::Func::getPreferencesValue('TABLEPLUGIN_TABLEATTRIBUTES');
colas@0
  1715
        $prefsAttrs = TWiki::Func::getPreferencesValue('TABLEATTRIBUTES');
colas@0
  1716
        _setDefaults();
colas@0
  1717
colas@0
  1718
        $TWiki::Plugins::TablePlugin::initialised = 1;
colas@0
  1719
    }
colas@0
  1720
colas@0
  1721
    undef $initSort;
colas@0
  1722
    $insideTABLE = 0;
colas@0
  1723
colas@0
  1724
    my $defaultSort = $sortAllTables;
colas@0
  1725
colas@0
  1726
    my $acceptable = $sortAllTables;
colas@0
  1727
    my @lines = split( /\r?\n/, $_[0] );
colas@0
  1728
    for (@lines) {
colas@0
  1729
        if (
colas@0
  1730
s/%TABLE(?:{(.*?)})?%/_parseParameters(1,undef,TWiki::Func::extractParameters($1))/se
colas@0
  1731
          )
colas@0
  1732
        {
colas@0
  1733
            $acceptable = 1;
colas@0
  1734
        }
colas@0
  1735
        elsif (s/^(\s*)\|(.*\|\s*)$/_processTableRow($1,$2)/eo) {
colas@0
  1736
            $insideTABLE = 1;
colas@0
  1737
        }
colas@0
  1738
        elsif ($insideTABLE) {
colas@0
  1739
            $_           = emitTable() . $_;
colas@0
  1740
            $insideTABLE = 0;
colas@0
  1741
            undef $initSort;
colas@0
  1742
            $sortAllTables = $defaultSort;
colas@0
  1743
            $acceptable    = $defaultSort;
colas@0
  1744
        }
colas@0
  1745
    }
colas@0
  1746
    $_[0] = join( "\n", @lines );
colas@0
  1747
colas@0
  1748
    if ($insideTABLE) {
colas@0
  1749
        $_[0] .= emitTable();
colas@0
  1750
    }
colas@0
  1751
}
colas@0
  1752
colas@0
  1753
1;