jenkins-bot has submitted this change and it was merged.
Change subject: (extension/TimelineTable) Refactoring and cleanup
......................................................................
(extension/TimelineTable) Refactoring and cleanup
Use classes for hooks registration and parser.
Use php's 'strftime' function to localize months names.
Use Html::element to create HTML table.
Fix format.
Option to hide years.
v2.0 updates:
-More flexible input date parsing
-Week level
-Arbitrary headers/footers
-depth parameter
-Rebase master to include changes to i18n
-Add output type to error messages
v2.1 updates:
-Hour/minute/second level
-Support for vertical tables
-Some memory cleanup
Change-Id: I742c54a128ae542ae0d0f4e59e315157819eaee3
---
A TimelineTable.DateDiffHelper.php
A TimelineTable.Event.php
A TimelineTable.Hooks.php
A TimelineTable.Table.php
D TimelineTable.body.php
D TimelineTable.i18n.php
M TimelineTable.php
A TimelineTable.style.css
M i18n/en.json
D i18n/fr.json
A i18n/qqq.json
11 files changed, 1,691 insertions(+), 736 deletions(-)
Approvals:
Siebrand: Looks good to me, approved
jenkins-bot: Verified
diff --git a/TimelineTable.DateDiffHelper.php b/TimelineTable.DateDiffHelper.php
new file mode 100644
index 0000000..3b36c95
--- /dev/null
+++ b/TimelineTable.DateDiffHelper.php
@@ -0,0 +1,182 @@
+<?php
+
+/**
+ * Class with constant descriptors for depth.
+ */
+abstract class TimelineTableDepthDesc {
+
+ const Year = 0;
+ const Month = 1;
+ const Week = 2;
+ const Day = 3;
+ const Hour = 4;
+ const Minute = 5;
+ const Second = 6;
+
+ static public function decodeDepthDesc( $str ) {
+ switch ( strtolower( $str ) ) {
+ case "year":
+ return self::Year;
+ case "month":
+ return self::Month;
+ case "week":
+ return self::Week;
+ case "day":
+ return self::Day;
+ case "hour":
+ return self::Hour;
+ case "minute":
+ return self::Minute;
+ case "second":
+ return self::Second;
+ default:
+ return null;
+ }
+ }
+}
+
+/**
+ * Helper class for date operations:
+ * -calculate the number of cells between two dates depending on the depth
+ * -get first/last day of a given year
+ */
+class TimelineTableDateDiffHelper {
+
+ /**
+ * Get first day in year (modify input $date)
+ */
+ public static function getFirstDay( &$date, $year ) {
+ $date->modify( "first day of january " . $year );
+ $date->setTime(0, 0, 0);
+ }
+
+ /**
+ * Get last day in year (modify input $date)
+ */
+ public static function getLastDay( &$date, $year ) {
+ $date->modify( "last day of december " . $year );
+ $date->setTime(23, 59, 59);
+ }
+
+ /**
+ * Calculate the number of cells between two dates (public interface)
+ */
+ public static function getNumCells( $date1, $date2, $depth ) {
+
+ if ( !is_a( $date1, "DateTime" ) || !is_a( $date2, "DateTime" )
) {
+ return null;
+ }
+ $startYear = intval( $date1->format( "Y" ) );
+ $endYear = intval( $date2->format( "Y" ) );
+
+ switch ( $depth ) {
+
+ case TimelineTableDepthDesc::Year:
+ $int = $date1->diff( $date2 );
+ $nYears = $endYear - $startYear + 1;
+
+ return ( $int->invert ? -1 : 1 ) * $nYears;
+
+ case TimelineTableDepthDesc::Month:
+ $m1 = intval( $date1->format( "n" ) );
+ $m2 = intval( $date2->format( "n" ) );
+ if ( $startYear == $endYear ) {
+ $nMonths = $m2 - $m1 + 1;
+ } else {
+ $datecur = clone $date1;
+ self::getLastDay( $datecur, $startYear
);
+ $nMonths = 0;
+
+ $nMonths += intval( $datecur->format(
"n" ) ) -
+ $m1 + 1;
+ $nMonths += 12 * ( $endYear -
$startYear - 1 );
+ $nMonths += intval( $date2->format( "n"
) );
+ }
+ $int = $date1->diff( $date2 );
+
+ return ( $int->invert ? -1 : 1 ) * $nMonths;
+
+ case TimelineTableDepthDesc::Week:
+
+ if ( $startYear == $endYear ) {
+ $w1 = intval( $date1->format( "W" ) );
+ $w2 = intval( $date2->format( "W" ) );
+ if ( $date1 <= $date2 ) {
+ if ( $w1 <= $w2 ) {
+ $nWeeks = $w2 - $w1 + 1;
+ } else {
+ // Wrap around: last
day in current year is in the first
+ // week of the
following year
+ $datecur = new
DateTime( $startYear . "-12-28" );
+ $nWeeks = intval(
$datecur->format( "W" ) ) - $w1 + 1;
+ }
+ } else {
+ if ( $w1 >= $w2 ) {
+ $nWeeks = $w2 - $w1 + 1;
+ } else {
+ // Wrap around: last
day in current year is in the first
+ // week of the
following year
+ $datecur = new
DateTime( $startYear . "-12-28" );
+ $nWeeks = intval(
$datecur->format( "W" ) ) - $w2 + 1;
+ }
+ }
+ } else {
+ $datecur = new DateTime( $startYear .
"-12-28" );
+ $nWeeks = 0;
+
+ if ( $date1 < $datecur ) {
+ $nWeeks += intval(
$datecur->format( "W" ) ) -
+ intval( $date1->format(
"W" ) ) + 1;
+ }
+
+ for ( $y = $startYear + 1; $y <
$endYear; $y++ ) {
+ $datecur = new DateTime( $y .
"-12-28" );
+ $nWeeks += intval(
$datecur->format( "W" ) );
+ }
+ $nWeeks += intval( $date2->format( "W"
) );
+ }
+ $int = $date1->diff( $date2 );
+
+ return ( $int->invert ? -1 : 1 ) * $nWeeks;
+
+ case TimelineTableDepthDesc::Day:
+ $int = $date1->diff( $date2 );
+ $nDays = $int->days + 1;
+
+ return ( $int->invert ? -1 : 1 ) * $nDays;
+
+ case TimelineTableDepthDesc::Hour:
+ $int = $date1->diff( $date2 );
+ $h1 = intval( $date1->format( "H" ) );
+ $h2 = intval( $date2->format( "H" ) );
+ $nHours = $int->days * 24 + $h2 - $h1 + 1;
+
+ return ( $int->invert ? -1 : 1 ) * $nHours;
+
+ case TimelineTableDepthDesc::Minute:
+ $int = $date1->diff( $date2 );
+ $h1 = intval( $date1->format( "H" ) );
+ $h2 = intval( $date2->format( "H" ) );
+ $m1 = intval( $date1->format( "i" ) );
+ $m2 = intval( $date2->format( "i" ) );
+ $nMinutes = $int->days * 24 * 60 + ($h2 - $h1)
* 60 +
+ $m2 - $m1 + 1;
+
+ return ( $int->invert ? -1 : 1 ) * $nMinutes;
+
+ case TimelineTableDepthDesc::Second:
+ $int = $date1->diff( $date2 );
+ $h1 = intval( $date1->format( "H" ) );
+ $h2 = intval( $date2->format( "H" ) );
+ $m1 = intval( $date1->format( "i" ) );
+ $m2 = intval( $date2->format( "i" ) );
+ $s1 = intval( $date1->format( "s" ) );
+ $s2 = intval( $date2->format( "s" ) );
+ $nSeconds = $int->days * 24 * 3600 + ($h2 -
$h1) * 3600 +
+ ($m2 - $m1) * 60 + $s2 - $s1 + 1;
+
+ return ( $int->invert ? -1 : 1 ) * $nSeconds;
+ }
+ }
+}
+
diff --git a/TimelineTable.Event.php b/TimelineTable.Event.php
new file mode 100644
index 0000000..70cb616
--- /dev/null
+++ b/TimelineTable.Event.php
@@ -0,0 +1,226 @@
+<?php
+
+/**
+ * TimelineTable Event object
+ */
+class TimelineTableEvent {
+
+ /// Start date
+ private $startDate;
+
+ /// End date
+ private $endDate;
+
+ /// Number of cells in block (only used for freetime block)
+ private $nCells;
+
+ /// Event description
+ private $text;
+
+ /// Event comment
+ private $comment;
+
+ /// Lenght of event text (for headers)
+ private $substr;
+
+ /// Cell type (td/th)
+ private $cellType;
+
+ /// Tooltip for event cell
+ private $tooltip;
+
+ /// Custom CSS-style for event cell
+ private $cellCSSStyle;
+
+ /// CSS class for event block
+ private $cellCSSClass;
+
+ /// Event validity
+ private $errMsg = "";
+
+ /**
+ * Parse event from string
+ */
+ public function parse( $input, $separator ) {
+
+ $fields = explode( $separator, trim( $input ) );
+ $nFields = count( $fields );
+ if ( $nFields >= 2 ) {
+
+ // If date is YYYY (old style date), add "-01-01" to
make it
+ // parseable by DateTime
+ if ( preg_match( "([0-9]{4})", $fields[0] ) ) {
+ $fields[0] .= "-01-01";
+ }
+ // Parse date (return exception message on failure)
+ try {
+ $this->startDate = new DateTime( $fields[0] );
+ } catch ( Exception $e ) {
+ $this->errMsg =
+ wfMessage(
'timelinetable-error-parsestart',
+ $e->getMessage() )->escaped();
+
+ return false;
+ }
+ // Process second date entry
+ if ( preg_match( "([0-9]{4})", $fields[1] ) ) {
+ $fields[1] .= "-01-01";
+ }
+ try {
+ $this->endDate = new DateTime( $fields[1] );
+ } catch ( Exception $e ) {
+ $this->errMsg =
+ wfMessage(
'timelinetable-error-parseend',
+ $e->getMessage() )->escaped();
+
+ return false;
+ }
+
+ // Check that startDate is before endDate
+ if ( $this->startDate > $this->endDate ) {
+ $this->errMsg =
+ wfMessage(
'timelinetable-error-negdate',
+ $input )->escaped();
+
+ return false;
+ }
+
+ // Read event text / comment / CSS style
+ if ( $nFields > 2 ) {
+ $this->text = $fields[2];
+ if ( $nFields > 3 ) {
+ $this->comment = $fields[3];
+ if ( $nFields > 4 ) {
+ $this->cellCSSStyle =
$fields[4];
+ }
+ }
+ }
+
+ // Set other fields
+ $this->cellCSSClass = 'tl_event';
+ $this->cellType = 'td';
+ $this->substr = 0;
+ $this->tooltip = $this->startDate->format( "Y-m-d" ) .
" / " .
+ $this->endDate->format( "Y-m-d" );
+ } else {
+ // Need at least two dates (start/end) to parse event
+ $this->errMsg =
+ wfMessage( 'timelinetable-error-parseargs'
)->escaped();
+
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Create event for table header/footer (e.g. year/month/week/day
header)
+ */
+ public function createEvent( $t_startDate, $t_endDate, $t_text,
$t_tooltip,
+ $t_class, $t_type, $t_substr = 0 ) {
+ $this->startDate = $t_startDate;
+ $this->endDate = $t_endDate;
+ $this->text = $t_text;
+ $this->tooltip = $t_tooltip;
+ $this->cellCSSClass = $t_class;
+ $this->cellType = $t_type;
+ $this->substr = $t_substr;
+ }
+
+ /**
+ * Create "event" block for free-time (determined by the number of cells
+ * instead of start/end dates)
+ */
+ public function createEventBlock( $t_nCells, $t_text, $t_tooltip,
+ $t_class ) {
+ $this->nCells = $t_nCells;
+ $this->text = $t_text;
+ $this->tooltip = $t_tooltip;
+ $this->cellCSSClass = $t_class;
+ $this->cellType = 'td';
+ $this->substr = 0;
+ }
+
+ /**
+ * Test invalid event
+ */
+ public function isValid() {
+ return strlen( $this->errMsg ) == 0;
+ }
+
+ /**
+ * Get length of event (in number of cells for desired depth)
+ */
+ public function getNumCells( $depth ) {
+ // Determine number of cells in current block
+ if ( $this->nCells > 0 ) {
+ $nEventCells = $this->nCells;
+ } else {
+ $nEventCells = TimelineTableDateDiffHelper::getNumCells(
+ $this->startDate, $this->endDate, $depth );
+ if ( $nEventCells === null ) {
+ wfDebugLog( "", "Trying to render empty
event\n" );
+ }
+ }
+ return $nEventCells;
+ }
+
+ /**
+ * Render HTML cell in table
+ */
+ public function render( $parser, $depth, $flagVert = false ) {
+
+ $spanDir = ( $flagVert ) ? 'rowspan' : 'colspan';
+
+ // Determine number of cells in current block
+ $nEventCells = $this->getNumCells( $depth );
+
+ // Create the event cell
+ $cellopts = array( $spanDir => $nEventCells,
+ 'class' => $this->cellCSSClass );
+ if ( strcmp( trim( $this->cellCSSStyle ), "" ) ) {
+ $cellopts['style'] = htmlspecialchars(
$this->cellCSSStyle );
+ }
+ if ( strcmp( trim( $this->tooltip ), "" ) ) {
+ $cellopts['title'] = htmlspecialchars( $this->tooltip );
+ }
+ $celltext = $parser->recursiveTagParse( $this->text );
+
+ // Add comment field ($substr should not be defined when
$comment is,
+ // $substr is for headers only)
+ if ( strcmp( trim( $this->comment ), "" ) ) {
+ $celltext .= '<br />(';
+ $parsed_comment = $parser->recursiveTagParse(
$this->comment );
+ $celltext .= $parsed_comment;
+ $celltext .= ')';
+ } elseif ( $this->substr > 0 ) {
+ // Perform substring if necessary
+ $celltext = substr( $celltext, 0, $this->substr );
+ }
+
+ // Create table cell
+ return Html::rawElement( $this->cellType, $cellopts, $celltext
);
+ }
+
+ /**
+ * Accessor for error message
+ */
+ public function getErrorMsg() {
+ return $this->errMsg;
+ }
+
+ /**
+ * Accessor for start date
+ */
+ public function getStartDate() {
+ return $this->startDate;
+ }
+
+ /**
+ * Accessor for end date
+ */
+ public function getEndDate() {
+ return $this->endDate;
+ }
+}
+
diff --git a/TimelineTable.Hooks.php b/TimelineTable.Hooks.php
new file mode 100644
index 0000000..b3000d8
--- /dev/null
+++ b/TimelineTable.Hooks.php
@@ -0,0 +1,353 @@
+<?php
+
+/**
+ * TimelineTable hooks and parser
+ */
+class TimelineTableHooks {
+
+ /**
+ * Register <timelinetable> hook
+ */
+ public static function efTimelineTableParserInit( $parser ) {
+ $parser->setHook( 'timelinetable',
+ 'TimelineTableHooks::efTimelineTableRender' );
+
+ return true;
+ }
+
+ /**
+ * After tidy
+ */
+ public static function efTimelineTableAfterTidy( & $parser, & $text ) {
+ // find markers in $text
+ // replace markers with actual output
+ global $markerList;
+ for ( $i = 0; $i < count( $markerList ); $i++ ) {
+ $text = preg_replace( '/xx-marker' . $i . '-xx/',
+ $markerList[$i], $text );
+ }
+
+ return true;
+ }
+
+ /**
+ * Define the html code as a marker, then change it back to text in
+ * 'efTimelineAfterTidy'. This is done to prevent the html code from
being
+ * modified afterwards.
+ */
+ private static function makeOutputString( $str ) {
+ global $markerList;
+ $makercount = count( $markerList );
+ $marker = "xx-marker" . $makercount . "-xx";
+ $markerList[$makercount] = $str;
+
+ return $marker;
+ }
+
+ /**
+ * Add <pre></pre> tags around error message and return
+ */
+ private static function makeErrorOutputString( $errMsg ) {
+ $errMsg = "TimelineTable:<br/>" . $errMsg;
+ $errMsg = Html::rawElement( 'pre', array(), $errMsg );
+
+ return self::makeOutputString( $errMsg );
+ }
+
+ /**
+ * Main function: parse input and create HTML table with events
+ */
+ public static function efTimelineTableRender( $input, array $args,
+ Parser $parser, PPFrame $frame = null
+ ) {
+
+ if ( is_null( $frame ) ) {
+ return self::makeOutputString( $input );
+ }
+
+ // Extract parameters from global variables
+ global $wgTimelineTableFieldSeparator;
+ global $wgTimelineTableDateSeparator;
+ global $wgTimelineTableLineSeparator;
+ global $wgTimelineTableAbbrMonth;
+ global $wgTimelineTableShortMonthLen;
+
+ // Format of month name
+ $monthFormat = $wgTimelineTableAbbrMonth ? "M" : "F";
+
+ // Parse tag arguments - title/caption
+ if ( isset( $args['title'] ) ) {
+ $title = $args['title'];
+ } else {
+ $title = "";
+ }
+ if ( isset( $args['caption'] ) ) {
+ $caption = $args['caption'];
+ } else {
+ // Try old style name: footer
+ if ( isset( $args['footer'] ) ) {
+ $caption = $args['footer'];
+ } else {
+ $caption = "";
+ }
+ }
+
+ // Use vertical table rendering?
+ $flagVertical = isset( $args['vertical'] );
+
+ // Get desired depth
+ if ( isset( $args['depth'] ) ) {
+ $depthStr = $args['depth'];
+ // Get depth descriptor from string
+ $depth = TimelineTableDepthDesc::decodeDepthDesc(
$depthStr );
+ } else {
+ // Parse first date entry (pre v.2.0 inputs)
+ $depth = TimelineTableTable::getDepthFromFirstDate(
$input );
+ }
+ if ( $depth === null ) {
+ $errStr = wfMessage( 'timelinetable-error-depth'
)->escaped();
+
+ return self::makeErrorOutputString( $errStr );
+ }
+
+ // Get default option table
+ // loc: 1 (header), -1 (footer), 0 (no line)
+ // format: see [http://www.php.net/manual/en/function.date.php]
+ // substr: number of characters to display (only used for month
with
+ // format F or M and days with format D or l)
+ $hDefaultOpts = array(
+ TimelineTableDepthDesc::Year => array( 'loc' => 1,
+ 'format' => 'Y',
+ 'substr' => 0 ),
+ TimelineTableDepthDesc::Month => array( 'loc' => 1,
+ 'format' => $monthFormat,
+ 'substr' => 0 ),
+ TimelineTableDepthDesc::Week => array( 'loc' => 0,
+ 'format' => 'W',
+ 'substr' => 0 ),
+ TimelineTableDepthDesc::Day => array( 'loc' => 1,
+ 'format' => 'j',
+ 'substr' => 0 ),
+ TimelineTableDepthDesc::Hour => array( 'loc' => 0,
+ 'format' => 'H',
+ 'substr' => 0 ),
+ TimelineTableDepthDesc::Minute => array( 'loc' => 0,
+ 'format' => 'i',
+ 'substr' => 0 ),
+ TimelineTableDepthDesc::Second => array( 'loc' => 0,
+ 'format' => 's',
+ 'substr' => 0 )
+ );
+ // Use weeks
+ if ( isset( $args['useweeks'] ) ||
+ $depth == TimelineTableDepthDesc::Week
+ ) {
+ $hDefaultOpts[TimelineTableDepthDesc::Week]['loc'] = 1;
+ }
+
+ // Header/footer list
+ $headerLines = array();
+ $footerLines = array();
+
+ if ( isset( $args['headers'] ) || isset( $args['footers'] ) ) {
+ // populate the headers (hi=0) / footers (hi=1) table:
+ // e.g. <timelinetable ... headers=Y/M-F-1 footers=D-l
... >
+ // Y => years in header
+ // M-F-1 => months in header (format "F", substring of
length 1)
+ // D-l => days in footer (format "l", keep full
string)
+ for ( $hi = 0; $hi < 2; $hi++ ) {
+ switch ( $hi ) {
+ case 0:
+ $HFLines = & $headerLines;
+ $argName = 'headers';
+ break;
+ case 1:
+ $HFLines = & $footerLines;
+ $argName = 'footers';
+ break;
+ }
+ if ( isset( $args[$argName] ) ) {
+ if ( strlen( trim( $args[$argName] ) )
== 0 ) {
+ // Skip if no entry (e.g.
headers="")
+ continue;
+ }
+ $fields = explode( '/', trim(
$args[$argName] ) );
+
+ foreach ( $fields as $hField ) {
+
+ $opts = explode( '-', trim(
$hField ) );
+ $nOpts = count( $opts );
+ if ( $nOpts > 0 ) {
+ switch ( strtolower(
$opts[0] ) ) {
+ case 'y':
+ $level
= TimelineTableDepthDesc::Year;
+ $css =
'tl_years';
+ break;
+ case 'm':
+ $level
= TimelineTableDepthDesc::Month;
+ $css =
'tl_months';
+ break;
+ case 'w':
+ $level
= TimelineTableDepthDesc::Week;
+ $css =
'tl_weeks';
+ break;
+ case 'd':
+ $level
= TimelineTableDepthDesc::Day;
+ $css =
'tl_days';
+ break;
+ case 'h':
+ $level
= TimelineTableDepthDesc::Hour;
+ $css =
'tl_days';
+ break;
+ case 'i':
+ $level
= TimelineTableDepthDesc::Minute;
+ $css =
'tl_days';
+ break;
+ case 's':
+ $level
= TimelineTableDepthDesc::Second;
+ $css =
'tl_days';
+ break;
+ default:
+ // Only
y/m/w/d in headers/footers argument
+ $eMsg =
wfMessage( 'timelinetable-error-hf',
+
$argName,
+
$args[$argName] )->escaped();
+
+ return
self::makeErrorOutputString( $eMsg );
+ break;
+ }
+ }
+
+ // Read format options
+ if ( $level > $depth ) {
+ continue;
+ }
+ if ( $nOpts > 1 ) {
+ $format = $opts[1];
+ } else {
+ $format =
$hDefaultOpts[$level]['format'];
+ }
+ if ( $nOpts > 2 ) {
+ $substr = $opts[2];
+ } else {
+ $substr =
$hDefaultOpts[$level]['substr'];
+ }
+
+ // Add header/footer to list
+ array_push( $HFLines, array(
'level' => $level,
+ 'format' => $format,
+ 'substr' => $substr,
+ 'cssclass' => $css ) );
+ }
+ }
+ }
+ } else {
+ // Old style options (pre v2.0)
+ $topLevel = TimelineTableDepthDesc::Year;
+ for ( $level = $topLevel; $level <= $depth; $level++ ) {
+
+ $loc = $hDefaultOpts[$level]['loc'];
+
+ if ( $loc != 0 ) {
+ $format =
$hDefaultOpts[$level]['format'];
+ $substr =
$hDefaultOpts[$level]['substr'];
+ $css = '';
+ $doContinue = false;
+ switch ( $level ) {
+ case
TimelineTableDepthDesc::Year:
+ if ( isset(
$args['noyears'] ) ) {
+ // Hide years
+ $doContinue =
true;
+ }
+ $css = 'tl_years';
+ break;
+ case
TimelineTableDepthDesc::Month:
+ if ( isset(
$args['nomonths'] ) ) {
+ // Hide months
+ $doContinue =
true;
+ }
+ if ( $depth <
TimelineTableDepthDesc::Day ) {
+ // Abbreviate
months
+ $substr =
$wgTimelineTableShortMonthLen;
+ }
+ $css = 'tl_months';
+ break;
+ case
TimelineTableDepthDesc::Week:
+ $css = 'tl_weeks';
+ break;
+ case
TimelineTableDepthDesc::Day:
+ if ( isset(
$args['daynames'] ) ) {
+ // Show day
names (if option passed)
+ $format = 'D';
+ }
+ $css = 'tl_days';
+ break;
+ case
TimelineTableDepthDesc::Hour:
+ $css = 'tl_days';
+ break;
+ case
TimelineTableDepthDesc::Minute:
+ $css = 'tl_days';
+ break;
+ case
TimelineTableDepthDesc::Second:
+ $css = 'tl_days';
+ break;
+ }
+ if ( $doContinue ) {
+ continue;
+ }
+ if ( $loc == 1 || $loc == 2 ) {
+ array_push( $headerLines,
array( 'level' => $level,
+ 'format' => $format,
+ 'substr' => $substr,
+ 'cssclass' => $css ) );
+ }
+ if ( $loc == -1 || $loc == 2 ) {
+ array_push( $footerLines,
array( 'level' => $level,
+ 'format' => $format,
+ 'substr' => $substr,
+ 'cssclass' => $css ) );
+ }
+ }
+ }
+ }
+
+ // Create table
+ $table = new TimelineTableTable( $title, $caption );
+
+ // Parse input (events)
+ $errParse = $table->parse( $input, $depth );
+
+ // Check parse output
+ if ( strcmp( $errParse, "" ) != 0 ) {
+ return self::makeErrorOutputString( $errParse );
+ }
+
+ // Setup headers
+ for ( $hi = 0; $hi < 2; $hi++ ) {
+ switch ( $hi ) {
+ case 0:
+ $HFLines = & $headerLines;
+ break;
+ case 1:
+ $HFLines = & $footerLines;
+ break;
+ }
+ foreach ( $HFLines as $hLines ) {
+
+ $level = $hLines['level'];
+ $format = $hLines['format'];
+ $substr = $hLines['substr'];
+ $cssClass = $hLines['cssclass'];
+ $table->addHeader( $level, $depth, $cssClass,
$hi == 0,
+ $format, $substr );
+ }
+ }
+
+ // Render table
+ $timeline_str = $table->render( $parser, $depth, $flagVertical
);
+
+ // Return output
+ return self::makeOutputString( $timeline_str );
+ }
+}
+
diff --git a/TimelineTable.Table.php b/TimelineTable.Table.php
new file mode 100644
index 0000000..7859531
--- /dev/null
+++ b/TimelineTable.Table.php
@@ -0,0 +1,720 @@
+<?php
+
+/**
+ * Main class handling input parsing and rendering of the table
+ */
+class TimelineTableTable {
+
+ /// Table title
+ public $tableTitle = "";
+
+ /// Table caption
+ public $tableCaption = "";
+
+ /// Table content: list of event lists (one list per table row)
+ public $tableLines = array();
+
+ /// Table header lines
+ public $tableHeaderLines = array();
+
+ /// Table footer lines
+ public $tableFooterLines = array();
+
+ /// Earliest start date
+ public $startDate;
+
+ /// Latest end date;
+ public $endDate;
+
+ /**
+ * Constructor
+ */
+ public function __construct( $title, $caption ) {
+ $this->tableTitle = $title;
+ $this->tableCaption = $caption;
+ }
+
+ /**
+ * Render HTML table
+ */
+ public function render( $parser, $depth, $flagVert = false ) {
+ if ( $flagVert ) {
+ return $this->renderVertical( $parser, $depth );
+ } else {
+ return $this->renderHorizontal( $parser, $depth );
+ }
+ }
+
+ /**
+ * Render table vertically
+ */
+ public function renderVertical( $parser, $depth ) {
+
+ // Total number of cells
+ $nTotalCells = TimelineTableDateDiffHelper::getNumCells(
+ $this->startDate, $this->endDate, $depth );
+
+ // Build helper arrays for transposition
+ $locTableHeader = array();
+ $locTableFooter = array();
+ $locTableEntry = array();
+ $idxMapHeader = array();
+ $idxMapFooter = array();
+ $idxMapentry = array();
+ if ( count( $this->tableHeaderLines ) > 0 ) {
+ $locTableHeader = array_fill( 0, count(
$this->tableHeaderLines ),
+ null );
+ $this->fillVertTable( $this->tableHeaderLines,
$nTotalCells,
+ $locTableHeader, $depth );
+ $idxMapHeader = array_fill( 0, count(
$this->tableHeaderLines ),
+ 0 );
+ }
+ if ( count( $this->tableFooterLines ) > 0 ) {
+ $locTableFooter = array_fill( 0, count(
$this->tableFooterLines ) ,
+ null );
+ $this->fillVertTable( $this->tableFooterLines,
$nTotalCells,
+ $locTableFooter, $depth );
+ $idxMapFooter = array_fill( 0, count(
$this->tableFooterLines ),
+ 0 );
+ }
+ if ( count( $this->tableLines ) > 0 ) {
+ $locTableEntry = array_fill( 0, count(
$this->tableLines ), null );
+ $this->fillVertTable( $this->tableLines, $nTotalCells,
+ $locTableEntry, $depth );
+ $idxMapEntry = array_fill( 0, count( $this->tableLines
), 0 );
+ }
+
+ // Start the table
+ $ts = Html::openElement( 'table', array( 'class' => 'tl_table'
) );
+
+ for ( $i = 0; $i < $nTotalCells; $i++ ) {
+ $ts .= Html::openElement( 'tr' );
+
+ if ( $i == 0 ) {
+ if ( !empty( $this->tableTitle ) ) {
+ $ts .= Html::element( 'th',
+ array( 'rowspan'
=> $nTotalCells,
+ 'class' =>
'tl_title' ),
+ $this->tableTitle
);
+ }
+ }
+
+ $ts .= $this->renderVertRow( $this->tableHeaderLines,
+ $locTableHeader, $i,
$idxMapHeader,
+ $parser, $depth, 'th' );
+ $ts .= $this->renderVertRow( $this->tableLines,
+ $locTableEntry, $i,
$idxMapEntry,
+ $parser, $depth, 'td' );
+ $ts .= $this->renderVertRow( $this->tableFooterLines,
+ $locTableFooter, $i,
$idxMapFooter,
+ $parser, $depth, 'th' );
+
+ if ( $i == 0 ) {
+ if ( !empty( $this->tableCaption ) ) {
+ $ts .= Html::element( 'td', array(
'rowspan' => $nTotalCells,
+
'class' => 'tl_foot' ),
+
$this->tableCaption );
+ }
+ }
+
+ $ts .= Html::closeElement( 'tr' );
+ }
+
+ // Finish table
+ $ts .= Html::closeElement( 'table' );
+
+ return $ts;
+ }
+
+ public function renderHorizontal( $parser, $depth ) {
+
+ // Total number of cells
+ $nTotalCells = TimelineTableDateDiffHelper::getNumCells(
+ $this->startDate, $this->endDate, $depth );
+
+ // Start the table
+ $ts = Html::openElement( 'table', array( 'class' => 'tl_table'
) );
+
+ // Header: title line + headers
+ if ( strlen( $this->tableTitle ) > 0 ||
+ count( $this->tableHeaderLines )
+ ) {
+
+ $ts .= Html::openElement( 'thead', array( 'class' =>
'tl_header' ) );
+ $ts .= $this->renderTitleCaption( $this->tableTitle,
$nTotalCells,
+ 'tl_title', 'th' );
+ $ts .= $this->renderHeaderFooter(
$this->tableHeaderLines,
+ 'tl_title', $depth, $parser,
+ 'th' );
+ $ts .= Html::closeElement( 'thead' );
+ }
+ // Footer: caption line + footers
+ if ( strlen( $this->tableCaption ) > 0 ||
+ count( $this->tableFooterLines )
+ ) {
+
+ // Header: title line + headers
+ $ts .= Html::openElement( 'tfoot', array( 'class' =>
'tl_footer' ) );
+ $ts .= $this->renderHeaderFooter(
$this->tableFooterLines,
+ 'tl_foot', $depth, $parser,
+ 'th' );
+ $ts .= $this->renderTitleCaption( $this->tableCaption,
$nTotalCells,
+ 'tl_foot', 'td' );
+ $ts .= Html::closeElement( 'tfoot' );
+ }
+
+ // Body: Events
+ $ts .= Html::openElement( 'tbody', array( 'class' => 'tl_body'
) );
+ foreach ( $this->tableLines as $hLine ) {
+ $ts .= Html::openElement( 'tr' );
+ foreach ( $hLine as $hEvent ) {
+ if ( $hEvent->isValid() ) {
+ $ts .= $hEvent->render( $parser, $depth
);
+ } else {
+ $errMsg = $hEvent->getErrorMsg();
+ $ts .= Html::element( 'td', null,
$errMsg );
+ break;
+ }
+ }
+ $ts .= Html::closeElement( 'tr' );
+ }
+ $ts .= Html::closeElement( 'tbody' );
+
+ // Finish table
+ $ts .= Html::closeElement( 'table' );
+
+ return $ts;
+ }
+
+ /**
+ * Render title/caption line
+ */
+ private function renderTitleCaption( $HFText, $nTotalCells, $CSSclass,
+ $type
+ ) {
+ $ts = "";
+ if ( !empty( $HFText ) ) {
+
+ $ts .= Html::openElement( 'tr' );
+ $ts .= Html::element( $type, array( 'colspan' =>
$nTotalCells,
+ 'class' => $CSSclass ),
+ $HFText );
+ $ts .= Html::closeElement( 'tr' );
+ }
+
+ return $ts;
+ }
+
+ /**
+ * Render header/footer sections
+ */
+ private function renderHeaderFooter( &$tableHFLines, $CSSclass, $depth,
+ $parser, $HTMLtype ) {
+ $ts = "";
+
+ if ( count( $tableHFLines ) > 0 ) {
+ // Header/footer cells
+ foreach ( $tableHFLines as $hLine ) {
+ $ts .= Html::openElement( 'tr' );
+ foreach ( $hLine as $hEvent ) {
+ if ( $hEvent->isValid() ) {
+ $ts .= $hEvent->render(
$parser, $depth );
+ } else {
+ $errMsg = $hEvent->getErrMsg();
+ $ts .= Html::element(
$HTMLtype, null, $errMsg );
+ break;
+ }
+ }
+ $ts .= Html::closeElement( 'tr' );
+ }
+ }
+
+ return $ts;
+ }
+
+ /**
+ * Create helper table for vertical table
+ */
+ private function fillVertTable( $tableLines, $nTotalCells,
+ &$outLocTable, $depth ) {
+ for ( $hi = 0; $hi < count( $tableLines ); $hi++) {
+ $outLocTable[ $hi ] = array_fill( 0, $nTotalCells,
false);
+
+ $idxStart = 0;
+ for ( $ei = 0; $ei < count( $tableLines[ $hi ] );
$ei++) {
+ $event = $tableLines[ $hi ][ $ei ];
+ $nCells = $event->getNumCells( $depth );
+ $outLocTable[ $hi ][ $idxStart ] = true;
+ $idxStart += $nCells;
+ }
+ }
+ }
+
+ /**
+ * Render single row of vertical table
+ */
+ private function renderVertRow( $tableLines, $locTable, $rowIdx,
&$idxMap,
+ $parser, $depth, $HTMLtype ) {
+ $ts = '';
+ for ( $hi = 0; $hi < count( $tableLines ); $hi++) {
+ if ( $locTable[ $hi ][ $rowIdx ] ) {
+ $hEvent = $tableLines[ $hi ][ $idxMap[ $hi ] ];
+ if ( $hEvent->isValid() ) {
+ $ts .= $hEvent->render( $parser,
$depth, true );
+ } else {
+ $errMsg = $hEvent->getErrMsg();
+ $ts .= Html::element( $HTMLtype, null,
$errMsg );
+ break;
+ }
+ $idxMap[ $hi ]++;
+ }
+ }
+ return $ts;
+ }
+
+
+ /**
+ * Parse timelinetable input
+ */
+ public function parse( $input, $depth ) {
+
+ // Extract parameters from global variables
+ global $wgTimelineTableFieldSeparator;
+ global $wgTimelineTableLineSeparator;
+ global $wgTimelineTableEventSeparator;
+
+ // Get lines
+ $lines = explode( $wgTimelineTableLineSeparator, trim( $input )
);
+
+ // Get lists of events in each line
+ $flagFirstEvent = true;
+ $k = 0;
+ foreach ( $lines as $val ) {
+
+ $eventList = explode( $wgTimelineTableEventSeparator,
+ trim( $val ) );
+ $flagFirstEventLine = true;
+ $this->tableLines[$k] = array();
+
+ // Loop over events in the current line
+ foreach ( $eventList as $eventStr ) {
+
+ $event = new TimelineTableEvent();
+ // Parse event
+ if ( !$event->parse( $eventStr,
+ $wgTimelineTableFieldSeparator )
+ ) {
+ return $event->getErrorMsg();
+ }
+ $eventStartDate = $event->getStartDate();
+ $eventEndDate = $event->getEndDate();
+
+ // Update table first/last event if necessary
+ if ( $flagFirstEvent || $eventStartDate <
$this->startDate ) {
+ $this->startDate = $eventStartDate;
+ }
+ if ( $flagFirstEvent || $eventEndDate >
$this->endDate ) {
+ $this->endDate = $eventEndDate;
+ }
+
+ // Create free-time block between successive
events
+ if ( !$flagFirstEventLine ) {
+ $nCellsFreeTime =
TimelineTableDateDiffHelper::getNumCells(
+ $prevEndDate, $eventStartDate,
$depth );
+ // Remove two cells (exclusive range)
+ $nCellsFreeTime -= 2;
+ if ( $nCellsFreeTime < 0 ) {
+ // If events overlap or are not
in increasing order,
+ // return an error
+ return wfMessage(
'timelinetable-error-overlap',
+ $val )->escaped();
+ }
+ if ( $nCellsFreeTime > 0 ) {
+ $ftEvent = new
TimelineTableEvent();
+ $ftEvent->createEventBlock(
$nCellsFreeTime, '', '',
+ 'tl_freetime' );
+ array_push(
$this->tableLines[$k], $ftEvent );
+ }
+ }
+
+ // Add event to list
+ array_push( $this->tableLines[$k], $event );
+
+ // Prepare next entry
+ $flagFirstEvent = false;
+ $flagFirstEventLine = false;
+ $prevEndDate = $eventEndDate;
+ }
+ $k++;
+ }
+
+ // Add leading/trailing freetime blocks
+ for ( $k = 0; $k < count( $this->tableLines ); $k++ ) {
+
+ $firstEvent = $this->tableLines[$k][0];
+ $nEvents = count( $this->tableLines[$k] );
+ $lastEvent = $this->tableLines[$k][$nEvents - 1];
+ $firstDate = $firstEvent->getStartDate();
+ $lastDate = $lastEvent->getEndDate();
+
+ if ( $firstDate > $this->startDate ) {
+ // Leading freetime block
+ $nCellsFreeTime =
TimelineTableDateDiffHelper::getNumCells(
+ $this->startDate, $firstDate, $depth );
+ // Remove one cell (exclusive range)
+ $nCellsFreeTime--;
+ if ( $nCellsFreeTime < 0 ) {
+ // Something went wrong: there is a
date earlier than the
+ // overall start date, return an error
+ return wfMessage(
'timelinetable-error-free', $k )->escaped();
+ }
+ if ( $nCellsFreeTime > 0 ) {
+ $ftEvent = new TimelineTableEvent();
+ $ftEvent->createEventBlock(
$nCellsFreeTime, '', '',
+ 'tl_freetime' );
+ array_unshift( $this->tableLines[$k],
$ftEvent );
+ }
+ }
+
+ if ( $lastDate < $this->endDate ) {
+ // Trailing freetime block
+ $nCellsFreeTime =
TimelineTableDateDiffHelper::getNumCells(
+ $lastDate, $this->endDate, $depth );
+ // Remove one cell (exclusive range)
+ $nCellsFreeTime--;
+ if ( $nCellsFreeTime < 0 ) {
+ // Something went wrong: there is a
date later than the
+ // overall end date, return an error
+ return wfMessage(
'timelinetable-error-free', $k )->escaped();
+ }
+ if ( $nCellsFreeTime > 0 ) {
+ $ftEvent = new TimelineTableEvent();
+ $ftEvent->createEventBlock(
$nCellsFreeTime, '', '',
+ 'tl_freetime' );
+ array_push( $this->tableLines[$k],
$ftEvent );
+ }
+ }
+ }
+
+ // Return an empty string on succes (no error message)
+ return "";
+ }
+
+ /**
+ * Parse first date entry and guess depth (must be in format:
YYYY-MM-DD,
+ * where MM and DD may be omitted). Returns a TimelineTableDepthDesc
+ * constant or null if first date could not be parsed.
+ *
+ * This is for backward compatibility (pre 2.0) when depth is not
passed as
+ * an input argument.
+ */
+ public static function getDepthFromFirstDate( $input ) {
+
+ // Extract parameters from global variables
+ global $wgTimelineTableFieldSeparator;
+ global $wgTimelineTableLineSeparator;
+ global $wgTimelineTableEventSeparator;
+ global $wgTimelineTableDateSeparator;
+
+ // Get events
+ $lines = explode( $wgTimelineTableLineSeparator, trim( $input )
);
+
+ // Get lists of events in each line
+ $flagFirstEvent = true;
+ $k = 0;
+ foreach ( $lines as $val ) {
+
+ $eventList = explode( $wgTimelineTableEventSeparator,
+ trim( $val ) );
+ foreach ( $eventList as $eventStr ) {
+
+ $tmp = explode( $wgTimelineTableFieldSeparator,
+ trim( $eventStr ) );
+ if ( count( $tmp ) >= 2 ) {
+ $dateField = explode(
$wgTimelineTableDateSeparator,
+ trim( $tmp[0] ) );
+ switch ( count( $dateField ) ) {
+ case 1:
+ // Only one entry in
date, it must be a year (YYYY)
+ if ( preg_match(
"/^([0-9]{4})$/", $dateField[0] ) ) {
+ return
TimelineTableDepthDesc::Year;
+ } else {
+ return null;
+ }
+ case 2:
+ // Two entries in date,
it must be year-month (YYYY-MM)
+ if ( preg_match(
"/^([0-9]{4})$/", $dateField[0] ) &&
+ preg_match(
"/^([0-9]{1,2})$/", $dateField[1] )
+ ) {
+ return
TimelineTableDepthDesc::Month;
+ } else {
+ return null;
+ }
+ case 3:
+ // Three entries in
date, it must be year-month-day
+ // (YYYY-MM-DD)
+ if ( preg_match(
"/^([0-9]{4})$/", $dateField[0] ) &&
+ preg_match(
"/^([0-9]{1,2})$/", $dateField[1] ) &&
+ preg_match(
"/^([0-9]{1,2})$/", $dateField[2] )
+ ) {
+ return
TimelineTableDepthDesc::Day;
+ } else {
+ return null;
+ }
+ default:
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Create table lines for headers
+ */
+ public function addHeader( $level, $depth, $cssClass, $isHeader,
$format,
+ $substr
+ ) {
+
+ // Select header/footer mode
+ if ( $isHeader ) {
+ $k = count( $this->tableHeaderLines );
+ $this->tableHeaderLines[$k] = array();
+ } else {
+ $k = count( $this->tableFooterLines );
+ $this->tableFooterLines[$k] = array();
+ }
+
+ // Use header style for both header and footer
+ $type = 'th';
+
+ switch ( $level ) {
+
+ case TimelineTableDepthDesc::Year:
+ $curDate = clone $this->startDate;
+ while ( $curDate <= $this->endDate ) {
+ $str = $curDate->format( $format );
+ $hEvent = new TimelineTableEvent();
+ $startDate = clone $curDate;
+ $endDate = clone $curDate;
+ // TODO: make this more general
(first/last day of year)
+
TimelineTableDateDiffHelper::getLastDay( $endDate, $str );
+ if ( $endDate > $this->endDate ) {
+ $endDate = $this->endDate;
+ }
+ $hEvent->createEvent( $startDate,
$endDate, $str, '',
+ $cssClass, $type, $substr );
+ if ( $isHeader ) {
+ array_push(
$this->tableHeaderLines[$k], $hEvent );
+ } else {
+ array_push(
$this->tableFooterLines[$k], $hEvent );
+ }
+ $nextYearStr = strval( intval( $str ) +
1 );
+
TimelineTableDateDiffHelper::getFirstDay( $curDate,
+
$nextYearStr );
+ unset( $startDate );
+ unset( $endDate );
+ unset( $hEvent );
+ }
+ break;
+
+ case TimelineTableDepthDesc::Month:
+ $curDate = clone $this->startDate;
+ while ( $curDate <= $this->endDate ) {
+ $hEvent = new TimelineTableEvent();
+ $startDate = clone $curDate;
+ $endDate = clone $curDate;
+ $endDate->modify( 'last day of this
month' );
+ if ( $endDate > $this->endDate ) {
+ $endDate = $this->endDate;
+ }
+ $str = $curDate->format( $format );
+ $hEvent->createEvent( $startDate,
$endDate, $str, '',
+ $cssClass, $type, $substr );
+ if ( $isHeader ) {
+ array_push(
$this->tableHeaderLines[$k], $hEvent );
+ } else {
+ array_push(
$this->tableFooterLines[$k], $hEvent );
+ }
+ $curDate->modify( 'first day of next
month' );
+ unset( $startDate );
+ unset( $endDate );
+ unset( $hEvent );
+ }
+ break;
+
+ case TimelineTableDepthDesc::Day:
+ $curDate = clone $this->startDate;
+ // Update format if displaying day names and
timeline is longer than
+ // a week
+ if ( ( strcmp( $format, 'D' ) == 0 ||
+ strcmp( $format, 'l' ) == 0 ) &&
+ $this->endDate->diff( $curDate )->days
> 7
+ ) {
+ $flagAddDayNumber = true;
+ } else {
+ $flagAddDayNumber = false;
+ }
+ while ( $curDate <= $this->endDate ) {
+ $hEvent = new TimelineTableEvent();
+ $str = $curDate->format( $format );
+ if ( $flagAddDayNumber ) {
+ if ( $substr > 0 ) {
+ $str = substr( $str, 0,
$substr );
+ }
+ $str .= " " . $curDate->format(
"j" );
+ $substrEvent = 0;
+ } else {
+ $substrEvent = $substr;
+ }
+ $startDate = clone $curDate;
+ $endDate = clone $curDate;
+ $endDate->setTime(23, 59, 59);
+ if ( $endDate > $this->endDate ) {
+ $endDate = $this->endDate;
+ }
+ $hEvent->createEvent( $startDate,
$endDate, $str, '',
+ $cssClass, $type, $substrEvent
);
+ if ( $isHeader ) {
+ array_push(
$this->tableHeaderLines[$k], $hEvent );
+ } else {
+ array_push(
$this->tableFooterLines[$k], $hEvent );
+ }
+ unset( $curDate );
+ $curDate = clone $endDate;
+ $curDate->modify( '+1 second' );
+ unset( $startDate );
+ unset( $endDate );
+ unset( $hEvent );
+ }
+ break;
+
+ case TimelineTableDepthDesc::Week:
+ $curDate = clone $this->startDate;
+ while ( $curDate <= $this->endDate ) {
+ $hEvent = new TimelineTableEvent();
+ $startDate = clone $curDate;
+ // Move to first day of week (Monday)
+ $startDayIdx = $startDate->format( "N"
) - 1;
+ $endDate = clone $startDate;
+ $dayDiff = 6 - $startDayIdx;
+ $endDate->modify( '+' . $dayDiff . '
days' );
+ if ( $endDate > $this->endDate ) {
+ $endDate = $this->endDate;
+ }
+ $str = $curDate->format( $format );
+ $tooltip = $startDate->format( "Y-m-d"
) . " / " .
+ $endDate->format( "Y-m-d" );
+ $hEvent->createEvent( $startDate,
$endDate, $str, $tooltip,
+ $cssClass, $type, $substr );
+ if ( $isHeader ) {
+ array_push(
$this->tableHeaderLines[$k], $hEvent );
+ } else {
+ array_push(
$this->tableFooterLines[$k], $hEvent );
+ }
+ unset( $curDate );
+ $curDate = clone $endDate;
+ $curDate->modify( '+1 day' );
+ unset( $startDate );
+ unset( $endDate );
+ unset( $hEvent );
+ }
+ break;
+
+ case TimelineTableDepthDesc::Hour:
+ $curDate = clone $this->startDate;
+ while ( $curDate <= $this->endDate ) {
+ $hEvent = new TimelineTableEvent();
+ $startDate = clone $curDate;
+ $endDate = clone $curDate;
+ $curHour = intval( $endDate->format(
'H' ) );
+ $curMinute = intval( $endDate->format(
'i' ) );
+ $curSecond = intval( $endDate->format(
's' ) );
+ $endDate->setTime($curHour, 59, 59);
+ if ( $endDate > $this->endDate ) {
+ $endDate = $this->endDate;
+ }
+ $str = $curDate->format( $format );
+ $hEvent->createEvent( $startDate,
$endDate, $str, '',
+ $cssClass, $type, $substr );
+ if ( $isHeader ) {
+ array_push(
$this->tableHeaderLines[$k], $hEvent );
+ } else {
+ array_push(
$this->tableFooterLines[$k], $hEvent );
+ }
+ unset( $curDate );
+ $curDate = clone $endDate;
+ $curDate->modify( '+1 second' );
+ unset( $startDate );
+ unset( $endDate );
+ unset( $hEvent );
+ }
+ break;
+
+ case TimelineTableDepthDesc::Minute:
+ $curDate = clone $this->startDate;
+ while ( $curDate <= $this->endDate ) {
+ $hEvent = new TimelineTableEvent();
+ $startDate = clone $curDate;
+ $endDate = clone $curDate;
+ $curHour = intval( $endDate->format(
'h' ) );
+ $curMinute = intval( $endDate->format(
'i' ) );
+ $curSecond = intval( $endDate->format(
's' ) );
+ $endDate->setTime($curHour, $curMinute,
59);
+ if ( $endDate > $this->endDate ) {
+ $endDate = $this->endDate;
+ }
+ $str = $curDate->format( $format );
+ $hEvent->createEvent( $startDate,
$endDate, $str, '',
+ $cssClass, $type, $substr );
+ if ( $isHeader ) {
+ array_push(
$this->tableHeaderLines[$k], $hEvent );
+ } else {
+ array_push(
$this->tableFooterLines[$k], $hEvent );
+ }
+ unset( $curDate );
+ $curDate = clone $endDate;
+ $curDate->modify( '+1 second' );
+ unset( $startDate );
+ unset( $endDate );
+ unset( $hEvent );
+ }
+ break;
+
+ case TimelineTableDepthDesc::Second:
+ $curDate = clone $this->startDate;
+ while ( $curDate <= $this->endDate ) {
+ $hEvent = new TimelineTableEvent();
+ $startDate = clone $curDate;
+ $endDate = clone $curDate;
+ $curHour = intval( $endDate->format(
'h' ) );
+ $curMinute = intval( $endDate->format(
'i' ) );
+ $curSecond = intval( $endDate->format(
's' ) );
+ //$endDate->setTime($curHour,
$curMinute, 59);
+ if ( $endDate > $this->endDate ) {
+ $endDate = $this->endDate;
+ }
+ $str = $curDate->format( $format );
+ $hEvent->createEvent( $startDate,
$endDate, $str, '',
+ $cssClass, $type, $substr );
+ if ( $isHeader ) {
+ array_push(
$this->tableHeaderLines[$k], $hEvent );
+ } else {
+ array_push(
$this->tableFooterLines[$k], $hEvent );
+ }
+ unset( $curDate );
+ $curDate = clone $endDate;
+ $curDate->modify( '+1 second' );
+ unset( $startDate );
+ unset( $endDate );
+ unset( $hEvent );
+ }
+ break;
+ }
+ }
+}
+
diff --git a/TimelineTable.body.php b/TimelineTable.body.php
deleted file mode 100755
index 7bbaf2a..0000000
--- a/TimelineTable.body.php
+++ /dev/null
@@ -1,643 +0,0 @@
-<?php
-
-/**
- * Protect against register_globals vulnerabilities.
- * This line must be present before any global variable is referenced.
- */
-if( !defined( 'MEDIAWIKI' ) )
-{
- echo( "This is an extension to the MediaWiki package and cannot be run
standalone.\n" );
- die( -1 );
-}
-
-// Functions
-function isLeapYear($year) {
- return ($year%4==0) && ($year%100!=0) || ($year%400==0);
-}
-
-function nDaysMonth($month, $year) {
- // Check leap year
- switch ( $month ) {
- case ( $month==1 || $month==3 || $month==5 || $month==7 ||
$month==8 ||
- $month==10 || $month==12 ):
- return 31;
- break;
- case ( $month==4 || $month==6 || $month==9 || $month==11):
- return 30;
- break;
- case 2:
- if ( isLeapYear($year) )
- {
- return 29;
- }
- else
- {
- return 28;
- }
- break;
- }
-}
-
-function nDaysYear($year) {
- if ( isLeapYear($year) ) {
- return 366;
- } else {
- return 365;
- }
-}
-
-// Main function
-function efTimelineTableRender( $input, array $args, Parser $parser, PPFrame
$frame ) {
- // Parameters
- $wgTimelineTableLineSeparator = "\n"; // Separator for parsing lines
of the input.
- $wgTimelineTableFieldSeparator = "|"; // Separator for parsing fields
of a single
- // event.
- $wgTimelineTableDateSeparator = "-"; // Separator for parsing the
date of an event.
- $wgTimelineTableMaxCells = 100; // If the total length of the
timetable (in
- // days) is larger than this
value, do not
- // display days in table.
- $wgHTMLlr = "\n"; // Line return (in the rendered html
file).
- $wgHTMLtab = "\t"; // Tabulation (in the rendered html
file).
-
- // Extract parameters from global variables
-// global $wgTimelineTableFieldSeparator;
-// global $wgTimelineTableDateSeparator;
-// global $wgTimelineTableLineSeparator;
-// global $wgTimelineTableMaxCells;
-// global $wgHTMLlr;
-// global $wgHTMLtab;
-
- // Parse tag arguments
- //$title = $args['title']; // fix from MikaelLindmark
- //$footer = $args['footer'];
- if(isset($args['title'])) { $title = $args['title']; } else { $title
= ""; }
- if(isset($args['footer'])){ $footer = $args['footer']; } else { $footer
= ""; }
-
- // Get events
- $lines = explode($wgTimelineTableLineSeparator,trim($input));
-
- // ---------- Process years (get first and last) ----------
- // --------------------------------------------------------
- $allStartYear = 9999; // Should work for a while
- $allEndYear = -1;
- foreach ( $lines as $val ) {
- $tmp=explode($wgTimelineTableFieldSeparator,trim($val));
- if (count($tmp) >= 2) {
-
$year=explode($wgTimelineTableDateSeparator,trim($tmp[0]),2);
- if ( (int)($year[0])<$allStartYear ){$allStartYear =
(int)($year[0]);}
-
$year=explode($wgTimelineTableDateSeparator,trim($tmp[1]),2);
- if ( (int)($year[0])>$allEndYear ){$allEndYear =
(int)($year[0]);}
- }
- }
-
- // Number of years to display
- $nTotalYears = $allEndYear - $allStartYear + 1;
- // ---------- Process months (get first and last) ----------
- // ---------------------------------------------------------
- $allStartMonth = 13;
- $allEndMonth = 0;
- $flagShowMonths = true;
- foreach ( $lines as $val ) {
- $tmp=explode($wgTimelineTableFieldSeparator,trim($val));
- $eventStart = explode($wgTimelineTableDateSeparator,$tmp[0]);
- if ( sizeof($eventStart)>0 ) {
- $eventStartYear = $eventStart[0];
- } else {
- $flagShowMonths = false;
- }
- if ( sizeof($eventStart)>1 ) {
- $eventStartMonth = $eventStart[1];
- } else {
- $eventStartMonth = 0;
- $flagShowMonths = false;
- }
- $eventEnd = explode($wgTimelineTableDateSeparator,$tmp[1]);
- if ( sizeof($eventEnd)>0 ) {
- $eventEndYear = $eventEnd[0];
- } else {
- $flagShowMonths = false;
- }
- if ( sizeof($eventEnd)>1 ) {
- $eventEndMonth = $eventEnd[1];
- } else {
- $eventEndMonth = 0;
- $flagShowMonths = false;
- }
- $eventStartYear = ((int)$eventStartYear);
- $eventEndYear = ((int)$eventEndYear);
- $eventStartMonth = (int)$eventStartMonth;
- $eventEndMonth = (int)$eventEndMonth;
- if ( $eventStartMonth==0 || $eventEndMonth==0 ) {
- $flagShowMonths = false;
- }
- if ( $eventStartYear==$allStartYear ) {
- if ( $eventStartMonth<$allStartMonth ) {
- $allStartMonth = $eventStartMonth;
- }
- }
- if ( $eventEndYear==$allEndYear ) {
- if ( $eventEndMonth>$allEndMonth ) {
- $allEndMonth = $eventEndMonth;
- }
- }
- }
- $nMonths[] = 12 - $allStartMonth + 1;
- for ( $year=$allStartYear+1 ; $year<$allEndYear ; $year++ ) {
- $nMonths[] = 12;
- }
- $nMonths[] = $allEndMonth;
- // $nTotalMonths contains the total number of months over the time range
- $nTotalMonths = array_sum($nMonths);
-
- // ---------- Process days (get first and last) ----------
- // -------------------------------------------------------
- $allStartDay = 32;
- $allEndDay = 0;
- $flagShowDays = $flagShowMonths? true: false;
- foreach ( $lines as $val ) {
- $tmp = explode($wgTimelineTableFieldSeparator,trim($val));
- if (count($tmp) < 2) {
- continue;
- }
- $eventStarttmp = explode($wgTimelineTableDateSeparator,$tmp[0]);
- if (count($eventStarttmp) < 3) {
- continue;
- }
- list($eventStartYear, $eventStartMonth, $eventStartDay) =
$eventStarttmp;
- list($eventEndYear, $eventEndMonth, $eventEndDay) =
- explode($wgTimelineTableDateSeparator,$tmp[1]);
- $eventStartYear = ((int)$eventStartYear);
- $eventEndYear = ((int)$eventEndYear);
- $eventStartMonth = (int)$eventStartMonth;
- $eventEndMonth = (int)$eventEndMonth;
- $eventStartDay = (int)$eventStartDay;
- $eventEndDay = (int)$eventEndDay;
- if ( $eventStartDay==0 || $eventEndDay==0 ) {
- $flagShowDays = false;
- }
- if ( $eventStartYear==$allStartYear &&
- $eventStartMonth==$allStartMonth ) {
- if ( $eventStartDay<$allStartDay ) {
- $allStartDay = $eventStartDay;
- }
- }
- if ( $eventEndYear==$allEndYear && $eventEndMonth==$allEndMonth
) {
- if ( $eventEndDay>$allEndDay ) {
- $allEndDay = $eventEndDay;
- }
- }
- }
- if ( $allStartYear==$allEndYear && $allStartMonth==$allEndMonth ) {
- $nDays[0] = $allEndDay - $allStartDay + 1;
- }
- elseif ( $allStartYear==$allEndYear ) {
- $nDays[0] = nDaysMonth($allStartMonth,$allStartYear) -
$allStartDay + 1;
- for ( $month=$allStartMonth+1 ; $month<$allEndMonth ; $month++
) {
- $nDays[] = nDaysMonth($month,$allStartYear);
- }
- } else {
- $nDays[0] = nDaysMonth($allStartMonth,$allStartYear) -
$allStartDay + 1;
- for ( $month=$allStartMonth+1 ; $month<=12 ; $month++ ) {
- $nDays[] = nDaysMonth($month,$allStartYear);
- }
- for ( $year=1 ; $year<$nTotalYears-1 ; $year++ ) {
- $year1 = $year + $allStartYear;
- for ( $month=1 ; $month<=12 ; $month++ ) {
- $nDays[] = nDaysMonth($month,$year1);
- }
- }
- for ( $month=1 ; $month<$allEndMonth ; $month++ ) {
- $nDays[] = nDaysMonth($month,$allEndYear);
- }
- }
- $nDays[] = $allEndDay;
- // $nTotalDays contains the total number of days over the time range
- $nTotalDays = array_sum($nDays);
-
- // ----- Display level (days, months or years) -----
- // -------------------------------------------------
- if ($flagShowMonths) {
- if ( $nTotalDays<$wgTimelineTableMaxCells && $flagShowDays ) {
- $flagShowDays = true;
- $monthList = array( "January" , "February" , "March" ,
"April" , "May" ,
- "June" , "July"
, "August" , "September" , "October" ,
- "November" ,
"December" );
- $nTotalCells = $nTotalDays;
- } else {
- $flagShowDays = false;
- $monthList = array( "J", "F", "M", "A", "M", "J", "J",
"A", "S", "O",
- "N", "D" );
- $nTotalCells = $nTotalMonths;
- }
- } else {
- $nTotalCells = $nTotalYears;
- }
-
- // ----- Span values -----
- // -----------------------
- // Number of cells in each year
- if ( $flagShowDays ) {
- for ( $year=0 ; $year<$nTotalYears ; $year++ ) {
- $year1 = $year + $allStartYear;
- if ( $year1!=$allStartYear && $year1!=$allEndYear ) {
- $nCellsYear[$year] = nDaysYear($year1);
- }
- elseif ( $year1==$allStartYear && $year1==$allEndYear )
{
- if ( $allStartMonth == $allEndMonth ) {
- $nCellsYear[$year] = $allEndDay -
$allStartDay + 1;
- } else {
- $nCellsYear[$year] =
nDaysMonth($allStartMonth,$year1) -
- $allStartDay + 1;
- for ( $month=$allStartMonth+1 ;
$month<=$allEndMonth ;
- $month++ ) {
- if ( $month==$allEndMonth ) {
- $nCellsYear[$year] +=
$allEndDay;
- } else {
- $nCellsYear[$year] +=
nDaysMonth($month,$year1);
- }
- }
- }
- }
- elseif ( $year1==$allStartYear ) {
- $nCellsYear[$year] =
nDaysMonth($allStartMonth,$year1) -
- $allStartDay + 1;
- for ( $month=$allStartMonth+1 ; $month<=12 ;
$month++ ) {
- if ( $month==$allEndMonth ) {
- $nCellsYear[$year]+=$allEndDay;
- } else {
- $nCellsYear[$year] +=
nDaysMonth($month,$year1);
- }
- }
- }
- elseif ( $year1==$allEndYear ) {
- $nCellsYear[$year]=0;
- for ( $month=1 ; $month<=$allEndMonth ;
$month++ ) {
- if ( $month==$allEndMonth ) {
- $nCellsYear[$year] +=
$allEndDay;
- } else {
- $nCellsYear[$year] +=
nDaysMonth($month,$year1);
- }
- }
- }
- }
- }
- elseif($flagShowMonths) {
- $nCellsYear[0] = 12 - $allStartMonth + 1;
- for ( $year=1 ; $year<$nTotalYears-1 ; $year++) {
- $nCellsYear[$year]=12;
- }
- $nCellsYear[$nTotalYears-1] = $allEndMonth;
- } else {
- for ( $year=0 ; $year<$nTotalYears ; $year++ ) {
- $nCellsYear[$year] = 1;
- }
- }
- // Number of cells in each month (only when displaying days)
- if ( $flagShowDays ) {
- for ( $year=0 ; $year<$nTotalYears ; $year++ ) {
- $year1 = $year + $allStartYear;
- if ( $year1 == $allStartYear ) {
- $monthStart = $allStartMonth;
- } else {
- $monthStart = 1;
- }
- if ( $year1 == $allEndYear ) {
- $monthEnd = $allEndMonth;
- } else {
- $monthEnd = 12;
- }
- for ( $month=$monthStart ; $month<=$monthEnd ; $month++
) {
- if ( $year==0 && $month==$allStartMonth ) {
- if ( $allStartMonth==$allEndMonth &&
- $allStartYear==$allEndYear ) {
- $nCellsMonth[] = $allEndDay -
$allStartDay + 1;
- } else {
- $nCellsMonth[] =
nDaysMonth($month,$year1) -
- $allStartDay + 1;
- }
- }
- elseif ( $year==$nTotalYears-1 &&
$month==$allEndMonth ) {
- $nCellsMonth[] = $allEndDay;
- } else {
- $nCellsMonth[] =
nDaysMonth($month,$year1);
- }
- }
- }
- }
-
-
//----------------------------------------------------------------------------
- // Create the timeline: $timeline_str will contain the html code for
the table
-
//----------------------------------------------------------------------------
-
- // Start the table
- $timeline_str = "<table class=tl_table>$wgHTMLlr";
- // Header: title line
- $timeline_str .= "$wgHTMLtab<thead class=tl_header>\n";
- $timeline_str .= "$wgHTMLtab$wgHTMLtab<tr>";
- $timeline_str .= "$wgHTMLlr$wgHTMLtab$wgHTMLtab$wgHTMLtab";
- $timeline_str .= '<th colspan="' . $nTotalCells;
- $timeline_str .= '" class=tl_title>';
- $timeline_str .= htmlspecialchars($title);
- $timeline_str .= "</th>$wgHTMLlr";
- $timeline_str .= "$wgHTMLtab$wgHTMLtab</tr>$wgHTMLlr";
-
- // Header: Years timeline
- $timeline_str .= "$wgHTMLtab$wgHTMLtab<tr>$wgHTMLlr";
- for ( $year=0 ; $year<$nTotalYears ; $year++ ) {
- $timeline_str .= "$wgHTMLtab$wgHTMLtab$wgHTMLtab";
- $timeline_str .= '<th colspan="' . $nCellsYear[$year];
- $timeline_str .= '" class=tl_years>';
- $timeline_str .= ($year + $allStartYear);
- $timeline_str .= "</th>$wgHTMLlr";
- }
- $timeline_str .= "$wgHTMLtab$wgHTMLtab</tr>$wgHTMLlr";
- if($flagShowMonths) {
- $timeline_str .= "$wgHTMLtab$wgHTMLtab<tr>$wgHTMLlr";
- // Header: Months
- $monthIdx = 0;
- for ( $year=0 ; $year<$nTotalYears ; $year++ ) {
- $year1 = $year + $allStartYear;
- if ( $year1 == $allStartYear ) {
- $monthStart = $allStartMonth;
- } else {
- $monthStart = 1;
- }
- if ( $year1 == $allEndYear ) {
- $monthEnd = $allEndMonth;
- } else {
- $monthEnd = 12;
- }
- for ( $month=$monthStart ; $month<=$monthEnd ; $month++
) {
- $timeline_str .=
"$wgHTMLtab$wgHTMLtab$wgHTMLtab";
- if ( $flagShowDays ) {
- $timeline_str .= '<th colspan="';
- $timeline_str .=
$nCellsMonth[$monthIdx];
- $timeline_str .= '" class=tl_months>';
- $monthIdx++;
- } else {
- $timeline_str .= '<th class=tl_months>';
- }
- $timeline_str .= $monthList[$month-1];
- $timeline_str .= "</th>$wgHTMLlr";
- }
- }
- $timeline_str .= "$wgHTMLtab$wgHTMLtab</tr>$wgHTMLlr";
- // Header: Days
- if ( $flagShowDays ) {
- $timeline_str .= "$wgHTMLtab$wgHTMLtab<tr>$wgHTMLlr";
- for ( $year=0 ; $year<$nTotalYears ; $year++ ) {
- $year1 = $year + $allStartYear;
- if ( $year1 == $allStartYear ) {
- $monthStart = $allStartMonth;
- } else {
- $monthStart = 1;
- }
- if ( $year1 == $allEndYear ) {
- $monthEnd = $allEndMonth;
- } else {
- $monthEnd = 12;
- }
- for ( $month=$monthStart ; $month<=$monthEnd ;
$month++ ) {
- if ($month==$allStartMonth &&
$year1==$allStartYear ) {
- $dayStart = $allStartDay;
- } else {
- $dayStart = 1;
- }
- if ($month==$allEndMonth &&
$year1==$allEndYear ) {
- $dayEnd = $allEndDay;
- } else {
- $dayEnd =
nDaysMonth($month,$year1);
- }
- for ( $day=$dayStart ; $day<=$dayEnd ;
$day++ ) {
- $timeline_str .=
"$wgHTMLtab$wgHTMLtab";
- $timeline_str .= "$wgHTMLtab";
- $timeline_str .= '<th
class=tl_days>';
- $timeline_str .= $day;
- $timeline_str .=
"</th>$wgHTMLlr";
- }
- }
- }
- $timeline_str .= "$wgHTMLtab$wgHTMLtab</tr>$wgHTMLlr";
- }
- }
- $timeline_str .= "$wgHTMLtab</thead>$wgHTMLlr";
-
- // Footer
- $timeline_str .= "$wgHTMLtab<tfoot class=tl_footer>";
- $timeline_str .= "$wgHTMLlr$wgHTMLtab$wgHTMLtab<tr>";
- $timeline_str .= "$wgHTMLlr$wgHTMLtab$wgHTMLtab$wgHTMLtab";
- $timeline_str .= '<td colspan="' . $nTotalCells;
- $timeline_str .= '" class=tl_foot>';
- $timeline_str .= htmlspecialchars($footer);
- $timeline_str .= "</td>$wgHTMLlr$wgHTMLtab$wgHTMLtab</tr>";
- $timeline_str .= "$wgHTMLlr$wgHTMLtab</tfoot>$wgHTMLlr";
-
- // Body: Events (display one event per row)
- $timeline_str .= "$wgHTMLtab<tbody class=tl_body>$wgHTMLlr";
- foreach ( $lines as $val ) {
- $lineTmp = explode($wgTimelineTableFieldSeparator,$val);
- $nFields = count($lineTmp);
- $comment = "";
- $cssStyle = "";
- switch ($nFields)
- {
- case 3:
- list($eventStartDate, $eventEndDate, $text) = $lineTmp;
- break;
- case 4:
- list($eventStartDate, $eventEndDate, $text, $comment) =
$lineTmp;
- break;
- case 5:
- list($eventStartDate, $eventEndDate, $text, $comment,
$cssStyle) =
- $lineTmp;
- break;
- default:
- continue;
- }
-
- // Parse the event dates and content
- $startDateTmp = explode($wgTimelineTableDateSeparator,
- $eventStartDate);
- $endDateTmp = explode($wgTimelineTableDateSeparator,
- $eventEndDate);
- if ( $flagShowDays ) {
- if (count($startDateTmp) < 3) {
- continue;
- }
- list($eventStartYear, $eventStartMonth, $eventStartDay)
=
- $startDateTmp;
- list($eventEndYear, $eventEndMonth, $eventEndDay) =
- $endDateTmp;
- $eventStartDay = (int)$eventStartDay;
- $eventEndDay = (int)$eventEndDay;
- $eventStartMonth = (int)$eventStartMonth;
- $eventEndMonth = (int)$eventEndMonth;
- } elseif ($flagShowMonths) {
- if (count($startDateTmp) < 2) {
- continue;
- }
- list($eventStartYear, $eventStartMonth) = $startDateTmp;
- list($eventEndYear, $eventEndMonth) = $endDateTmp;
- $eventStartMonth = (int)$eventStartMonth;
- $eventEndMonth = (int)$eventEndMonth;
- } else {
- $eventStartYear = $startDateTmp[0];
- $eventEndYear = $endDateTmp[0];
- }
- $eventStartYear = (int)$eventStartYear;
- $eventEndYear = (int)$eventEndYear;
-
- // Find the number of cells between the first column of the
timeline
- // table and the first cell of the event
- if ( $flagShowDays ) {
- $nPreviousCells = 0;
- $curY = $allStartYear;
- $curM = $allStartMonth;
- $curD = $allStartDay;
- while ( ($curY!=$eventStartYear ||
$curM!=$eventStartMonth ||
- $curD!=$eventStartDay) &&
$nPreviousCells<$nTotalCells ) {
- if ( $curM==12 &&
$curD==nDaysMonth($curM,$curY) ) {
- $curM = 1;
- $curD = 1;
- $curY++;
- }
- elseif ( $curD==nDaysMonth($curM,$curY) ) {
- $curM++;
- $curD = 1;
- } else {
- $curD++;
- }
- $nPreviousCells++;
- }
- $nEventCells = 1;
- // Find the length of the event (in days)
- while ( $curY!=$eventEndYear || $curM!=$eventEndMonth ||
- $curD!=$eventEndDay ) {
- if ( $curM==12 &&
$curD==nDaysMonth($curM,$curY) ) {
- $curM = 1;
- $curD = 1;
- $curY++;
- }
- elseif ( $curD==nDaysMonth($curM,$curY) ) {
- $curM++;
- $curD = 1;
- } else {
- $curD++;
- }
- $nEventCells++;
- }
- }
- elseif($flagShowMonths) { // if ( $flagShowDays )
- // $nPreviousCells = 0;
- // $curY = $allStartYear;
- // $curM = $allStartMonth;
- // while ( $curY!=$eventStartYear ||
$curM!=$eventStartMonth )
- // {
- // if ( $curM==12 )
- // {
- // $curM = 1;
- // $curY++;
- // }
- // else
- // {
- // $curM++;
- // }
- // $nPreviousCells++;
- // }
- $nPreviousCells = array_sum(array_slice($nMonths, 0,
- $eventStartYear-$allStartYear));
- $nPreviousCells += $eventStartMonth - 1;
- if ( $eventStartYear==$allStartYear ) {
- $nPreviousCells -= $allStartMonth;
- } else {
- $nPreviousCells -= 1;
- }
- if ( $nPreviousCells!=0 ) {
- $nPreviousCells++;
- }
- // Find the length of the event (in months)
- $nEventCells = 12 - $eventStartMonth + 1;
- $nEventCells = $nEventCells + $eventEndMonth;
- $nEventCells = $nEventCells +
12*($eventEndYear-$eventStartYear-1);
- } else {
- $nPreviousCells = $eventStartYear - $allStartYear;
- $nEventCells = $eventEndYear - $eventStartYear + 1;
- }
-
- // Define the number of cells between the end of the event and
the end
- // of the timeline table
- $nRemainingCells = $nTotalCells - $nPreviousCells -
$nEventCells;
-
- // Merge the cells before the event into a 'freetime' cell
- $timeline_str .= "$wgHTMLtab$wgHTMLtab<tr>$wgHTMLlr";
- if ( $nPreviousCells > 0 ) {
- $timeline_str .= "$wgHTMLtab$wgHTMLtab$wgHTMLtab";
- $timeline_str .= '<td colspan="' . $nPreviousCells;
- $timeline_str .= '" class=tl_freetime></td>';
- $timeline_str .= "$wgHTMLlr";
- }
- // Create the event cell
- $timeline_str .= "$wgHTMLtab$wgHTMLtab$wgHTMLtab";
- $timeline_str .= '<td colspan="' . $nEventCells;
- $timeline_str .= '" class=tl_event ';
- if ( strcmp(trim($cssStyle),"") ) {
- $timeline_str .= 'style="';
- $timeline_str .= htmlspecialchars($cssStyle) . '"';
- }
- $timeline_str .= ">";
- if( !defined( 'MEDIAWIKI' ) ) {
- $timeline_str .= htmlspecialchars($text);
- } else {
- $timeline_str .= $parser->recursiveTagParse($text);
- }
- if ( strcmp(trim($comment),"") ) {
- $timeline_str .= '<br />(';
- if( !defined( 'MEDIAWIKI' ) ) {
- $timeline_str .= htmlspecialchars($comment);
- } else {
- $parsed_comment =
$parser->recursiveTagParse($comment);
- $timeline_str .= $parsed_comment;
- }
- $timeline_str .= ')';
- }
- $timeline_str .= "</td>$wgHTMLlr";
- // Merge the cells after the event into a 'freetime' cell
- if ( $nRemainingCells > 0 ) {
- $timeline_str .= "$wgHTMLtab$wgHTMLtab$wgHTMLtab";
- $timeline_str .= '<td colspan="' . $nRemainingCells;
- $timeline_str .= '" class=tl_freetime></td>';
- $timeline_str .= "$wgHTMLlr";
- }
- $timeline_str .= "$wgHTMLtab$wgHTMLtab</tr>$wgHTMLlr";
- }
- $timeline_str .= "$wgHTMLtab</tbody>$wgHTMLlr";
-
- // Finish table
- $timeline_str .= "</table><br />$wgHTMLlr";
-
- // Define the html code as a marker, then change it back to text in
- // 'efTimelineAfterTidy'. This is done to prevent the html code from
being
- // modified afterwards.
- global $markerList;
- $makercount = count($markerList);
- $marker = "xx-marker".$makercount."-xx";
- $markerList[$makercount] = $timeline_str;
- return $marker;
-}
-
-function efTimelineTableAfterTidy(&$parser, &$text) {
- // find markers in $text
- // replace markers with actual output
- global $markerList;
- for ($i = 0; $i<count($markerList); $i++)
- $text = preg_replace('/xx-marker'.$i.'-xx/',$markerList[$i],$text);
- return true;
-}
-
-// TODO
-/*
- - Options
- - Check execution time
- - Check inputs (date order, etc.)
-*/
-
diff --git a/TimelineTable.i18n.php b/TimelineTable.i18n.php
deleted file mode 100755
index bd575db..0000000
--- a/TimelineTable.i18n.php
+++ /dev/null
@@ -1,35 +0,0 @@
-<?php
-/**
- * This is a backwards-compatibility shim, generated by:
- *
https://git.wikimedia.org/blob/mediawiki%2Fcore.git/HEAD/maintenance%2FgenerateJsonI18n.php
- *
- * Beginning with MediaWiki 1.23, translation strings are stored in json files,
- * and the EXTENSION.i18n.php file only exists to provide compatibility with
- * older releases of MediaWiki. For more information about this migration, see:
- * https://www.mediawiki.org/wiki/Requests_for_comment/Localisation_format
- *
- * This shim maintains compatibility back to MediaWiki 1.17.
- */
-$messages = array();
-if ( !function_exists( 'wfJsonI18nShime9fce3e1e1c94520' ) ) {
- function wfJsonI18nShime9fce3e1e1c94520( $cache, $code, &$cachedData ) {
- $codeSequence = array_merge( array( $code ),
$cachedData['fallbackSequence'] );
- foreach ( $codeSequence as $csCode ) {
- $fileName = dirname( __FILE__ ) . "/i18n/$csCode.json";
- if ( is_readable( $fileName ) ) {
- $data = FormatJson::decode( file_get_contents(
$fileName ), true );
- foreach ( array_keys( $data ) as $key ) {
- if ( $key === '' || $key[0] === '@' ) {
- unset( $data[$key] );
- }
- }
- $cachedData['messages'] = array_merge( $data,
$cachedData['messages'] );
- }
-
- $cachedData['deps'][] = new FileDependency( $fileName );
- }
- return true;
- }
-
- $GLOBALS['wgHooks']['LocalisationCacheRecache'][] =
'wfJsonI18nShime9fce3e1e1c94520';
-}
diff --git a/TimelineTable.php b/TimelineTable.php
index 80501c9..c12746a 100755
--- a/TimelineTable.php
+++ b/TimelineTable.php
@@ -4,7 +4,7 @@
* table.
*
* To activate this extension, add the following into your LocalSettings.php
file:
- * require_once('$IP/extensions/timelinetable.php');
+ * require_once('$IP/extensions/TimelineTable.php');
*
* @ingroup Extensions
* @author Thibault Marin
@@ -12,8 +12,27 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU General Public License
2.0 or later
*
* @revision
- * 1.7.1 -> 1.8.0
+ * 2.0 -> 2.1
+ * Fix internationalization, error messages
+ * Add support for time
+ * Vertical table option
+ * Requires MediaWiki 1.23 or later
+ * 1.8.1 -> 2.0
+ * Re-organize code into classes
+ * More flexible input format
([http://www.php.net/manual/en/datetime.formats.php])
+ * Allow multiple events per line (must be in increasing order and cannot
overlap)
+ * New option format to control headers display (e.g "headers=Y/M
footers=D-D")
+ * New selection of depth of table (e.g. <timelinetable ... depth=day ...>)
+ * Support for week level (display week number in year, week dates in tooltip)
+ * Tried to maintain backwards compatibility (except for the
+ * $wgTimelineTableMaxCells option which is now fully deprecated)
+ * 1.8 -> 1.8.1
+ * Option to show day names (daynames=1)
+ * Option to hide months (nomonths=1)
* Now using JSON i18n.
+ * 1.7.1 -> 1.8
+ * Cleanup for mediawiki review
+ * Option to hide years (noyears=1)
* 1.7 -> 1.7.1
* Rename tag to avoid conflict.
* 1.6 -> 1.7
@@ -30,38 +49,71 @@
* Protect against register_globals vulnerabilities.
* This line must be present before any global variable is referenced.
*/
-if( !defined( 'MEDIAWIKI' ) )
-{
+if ( !defined( 'MEDIAWIKI' ) ) {
echo( "This is an extension to the MediaWiki package and cannot be run
standalone.\n" );
die( -1 );
}
-// Extension credits that will show up on Special:Version
-$wgExtensionCredits['validextensionclass'][] = array(
- 'name' => __FILE__,
- 'version' => '1.8.0',
- 'author' => 'Thibault Marin',
- 'url' =>
'http://www.mediawiki.org/wiki/Extension:TimelineTable',
- 'description' => 'Create a table containing a timeline'
+/**
+ * Extension credits that will show up on Special:Version
+ */
+$wgExtensionCredits['parserhook'][] = array(
+ 'name' => 'TimelineTable',
+ 'path' => __FILE__,
+ 'version' => '2.1',
+ 'author' => 'Thibault Marin',
+ 'url' => 'http://www.mediawiki.org/wiki/Extension:TimelineTable',
+ 'descriptionmsg' => 'timelinetable-desc'
);
-//Avoid unstubbing $wgParser on setHook() too early on modern (1.12+) MW
-//versions, as per r35980
-if ( defined( 'MW_SUPPORTS_PARSERFIRSTCALLINIT' ) ) {
- $wgHooks['ParserFirstCallInit'][] = 'efTimelineTableParserInit';
- $wgHooks['ParserAfterTidy'][]='efTimelineTableAfterTidy';
- $wgExtensionFunctions[] = 'efTimelineTableParserInit';
-} else { // Otherwise do things the old fashioned way
- $wgHooks['ParserAfterTidy'][]='efTimelineTableAfterTidy';
- $wgExtensionFunctions[] = 'efTimelineTableParserInit';
-}
+/**
+ * Extension class
+ */
+$wgAutoloadClasses['TimelineTableHooks'] =
+ dirname( __FILE__ ) . '/TimelineTable.Hooks.php';
+$wgAutoloadClasses['TimelineTableEvent'] =
+ dirname( __FILE__ ) . '/TimelineTable.Event.php';
+$wgAutoloadClasses['TimelineTableDepthDesc'] =
+ dirname( __FILE__ ) . '/TimelineTable.DateDiffHelper.php';
+$wgAutoloadClasses['TimelineTableDateDiffHelper'] =
+ dirname( __FILE__ ) . '/TimelineTable.DateDiffHelper.php';
+$wgAutoloadClasses['TimelineTableTable'] =
+ dirname( __FILE__ ) . '/TimelineTable.Table.php';
-function efTimelineTableParserInit() {
- global $wgParser;
- $wgParser->setHook( 'timelinetable', 'efTimelineTableRender' );
- return true;
-}
+/**
+ * Register hooks
+ */
+$wgHooks['ParserFirstCallInit'][] =
+ 'TimelineTableHooks::efTimelineTableParserInit';
+$wgHooks['ParserAfterTidy'][] = 'TimelineTableHooks::efTimelineTableAfterTidy';
+/**
+ * Internationalization
+ */
$wgMessagesDirs['TimelineTable'] = __DIR__ . '/i18n';
-$wgExtensionMessagesFiles['TimelineTable'] = dirname( __FILE__ ) .
'/TimelineTable.i18n.php';
-require_once dirname(__FILE__) . '/TimelineTable.body.php';
+
+/**
+ * Parameters (modify in LocalSettings.php)
+ */
+
+// Separator for parsing lines of the input.
+$wgTimelineTableLineSeparator = PHP_EOL; //"\n";
+
+// Separator for parsing fields of a single event
+// (default "|" e.g. "date-start|date-end|text|style").
+$wgTimelineTableFieldSeparator = "|";
+
+// Separator for events within the same line
+$wgTimelineTableEventSeparator = "#";
+
+// Separator for parsing the date of an event (old style events, for backward
+// compatibility only).
+// (default: "-" e.g. "MM-DD-YYYY")
+$wgTimelineTableDateSeparator = "-";
+
+// Set this flag to true to abbreviate month names (see 'strftime' doc)
+$wgTimelineTableAbbrMonth = false;
+
+// Length of month/day name in compact mode (e.g. when displaying many years)
+$wgTimelineTableShortMonthLen = 1;
+
diff --git a/TimelineTable.style.css b/TimelineTable.style.css
new file mode 100644
index 0000000..e4214e7
--- /dev/null
+++ b/TimelineTable.style.css
@@ -0,0 +1,102 @@
+/* ***** ***** TimelineTable ***** ***** */
+table.tl_table {
+ border-width: thin;
+ border-spacing: 2px;
+ border-style: outset;
+ border-color: black;
+ border-collapse: separate;
+ background-color: white;
+ border-radius: 9px;
+}
+
+th.tl_title {
+ text-transform: uppercase;
+ text-align: center;
+ border-width: 1px;
+ padding: 1px;
+ border-style: outset;
+ border-color: blue;
+ background-color: rgb(243, 248, 252);
+ border-radius: 9px;
+}
+
+th.tl_years {
+ text-align: center;
+ font-style: italic;
+ border-width: 1px;
+ padding: 1px;
+ border-style: outset;
+ border-color: blue;
+ background-color: rgb(223, 228, 252);
+ border-radius: 4px;
+}
+
+th.tl_months {
+ text-align: center;
+ border-width: 1px;
+ padding: 1px;
+ border-style: outset;
+ border-color: blue;
+ background-color: rgb(243, 248, 252);
+ border-radius: 2px;
+}
+
+th.tl_days {
+ text-align: center;
+ border-width: 1px;
+ padding: 1px;
+ border-style: outset;
+ border-color: blue;
+ background-color: rgb(243, 248, 252);
+ border-radius: 2px;
+}
+
+th.tl_weeks {
+ text-align: center;
+ border-width: 1px;
+ padding: 1px;
+ border-style: outset;
+ border-color: blue;
+ background-color: rgb(243, 248, 252);
+ border-radius: 2px;
+}
+
+td.tl_freetime {
+ background-color: rgb(187, 210, 236);
+ border-width: 1px;
+ border-color: black;
+ border-style: inset;
+ border-radius: 7px;
+}
+
+td.tl_event {
+ text-align: center;
+ padding: 1px;
+ background-color: rgb(61, 114, 194);
+ border-width: 1px;
+ border-color: white;
+ border-style: inset;
+ color: white;
+ border-radius: 7px;
+ white-space: normal
+}
+
+td.tl_foot {
+ text-align: center;
+ padding: 1px;
+ background-color: rgb(243, 248, 252);
+ border-width: 1px;
+ border-color: blue;
+ border-style: ridge;
+ color: gray;
+ border-radius: 9px;
+}
+
+thead.tl_header {
+}
+
+tbody.tl_body {
+}
+
+tfoot.tl_footer {
+}
diff --git a/i18n/en.json b/i18n/en.json
index fd77382..e54d089 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -1,17 +1,16 @@
{
"@metadata": {
- "authors": []
+ "authors": [
+ "thibault marin"
+ ]
},
- "january": "January",
- "february": "February",
- "march": "March",
- "april": "April",
- "may": "May",
- "june": "June",
- "july": "July",
- "august": "August",
- "september": "September",
- "october": "October",
- "november": "November",
- "december": "December"
+ "timelinetable-desc": "Create a table containing a timeline",
+ "timelinetable-error-parsestart": "Error parsing event start: $1.",
+ "timelinetable-error-parseend": "Error parsing event end: $1.",
+ "timelinetable-error-parseargs": "Error parsing event dates: $1.",
+ "timelinetable-error-overlap": "Events on the same line overlap: $1.",
+ "timelinetable-error-free": "Error getting leading/trailing freetime block
at line $1.",
+ "timelinetable-error-depth": "Could not obtain depth parameter.",
+ "timelinetable-error-hf": "Error reading headers/footers: $1=$2.",
+ "timelinetable-error-negdate": "End date should be later than start date:
$1."
}
diff --git a/i18n/fr.json b/i18n/fr.json
deleted file mode 100644
index f4f8ee8..0000000
--- a/i18n/fr.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
- "@metadata": {
- "authors": []
- },
- "january": "Janvier",
- "february": "Fevrier",
- "march": "Mars",
- "april": "Avril",
- "may": "Mai",
- "june": "Juin",
- "july": "Juilet",
- "august": "Aout",
- "september": "Septembre",
- "october": "Octobre",
- "november": "Novembre",
- "december": "Decembre"
-}
diff --git a/i18n/qqq.json b/i18n/qqq.json
new file mode 100644
index 0000000..05ae768
--- /dev/null
+++ b/i18n/qqq.json
@@ -0,0 +1,16 @@
+{
+ "@metadata": {
+ "authors": [
+ "thibault marin"
+ ]
+ },
+ "timelinetable-desc": "{{desc}}",
+ "timelinetable-error-parsestart": "This is an error message shown when the
start of an event is not a proper date entry (parsing failed, $1 is the
exception message returned by DateTime constructor).",
+ "timelinetable-error-parseend": "This is an error message shown when the
end of an event is not a proper date entry (parsing failed, $1 is the exception
message returned by DateTime constructor).",
+ "timelinetable-error-parseargs": "This is an error message shown when the
start or end date could not be found.",
+ "timelinetable-error-overlap": "This is an error message shown when two
events in the same table line overlap ($1 is the input with overlapping
dates).",
+ "timelinetable-error-free": "This is an error message shown when something
went wrong during input parsing ($1 is index of the erroneous row in the input
table).",
+ "timelinetable-error-depth": "This is an error message shown when the
depth argument is missing and could not be guessed from the first date in the
table.",
+ "timelinetable-error-hf": "This is an error message shown when the
headers/footers option could not be parsed ($1 is the parameter name, $2 is the
parameter value).",
+ "timelinetable-error-negdate": "This is an error message shown when an
event has a start date later than the end date ($1 is the erroneous input
line)."
+}
--
To view, visit https://gerrit.wikimedia.org/r/38485
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I742c54a128ae542ae0d0f4e59e315157819eaee3
Gerrit-PatchSet: 18
Gerrit-Project: mediawiki/extensions/TimelineTable
Gerrit-Branch: master
Gerrit-Owner: thibaultmarin <[email protected]>
Gerrit-Reviewer: Addshore <[email protected]>
Gerrit-Reviewer: Raimond Spekking <[email protected]>
Gerrit-Reviewer: Siebrand <[email protected]>
Gerrit-Reviewer: Yaron Koren <[email protected]>
Gerrit-Reviewer: Yurik <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
Gerrit-Reviewer: thibaultmarin <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits