<?php
 
 
/**
 * ExampleExtension - this extension is an example that does nothing
 *
 * To activate this extension, add the following into your LocalSettings.php file:
 * require_once('$IP/extensions/Example.php');
 *
 * @ingroup Extensions
 * @author John Doe <john.doe@example.com>
 * @version 1.0
 * @link http://www.mediawiki.org/wiki/Extension:MyExtension Documentation
 * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
 */
 
/**
 * 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 );
}
 
// Extension credits that will show up on Special:Version    
$wgExtensionCredits['validextensionclass'][] = array(
	'name'         => 'Timeline',
	'version'      => '1.0',
	'author'       => 'Thibault Marin', 
	'url'          => 'http://www.mediawiki.org/wiki/Extension:MyExtension',
	'description'  => 'Create a table containing a timeline'
);
 
// Parameters
$wgTimelineLineSeparator="\n"; // Separator for parsing lines of the input.
$wgTimelineFieldSeparator="\|"; // Separator for parsing a single event (contained in a line).
$wgTimelineDateSeparator="-"; // Separator for parsing the date of an event.
 
//Avoid unstubbing $wgParser on setHook() too early on modern (1.12+) MW versions, as per r35980
if ( defined( 'MW_SUPPORTS_PARSERFIRSTCALLINIT' ) ) {
	$wgHooks['ParserFirstCallInit'][] = 'efTimelineParserInit';
	$wgHooks['ParserAfterTidy'][]='efTimelineAfterTidy';
	$wgExtensionFunctions[] = 'efTimelineParserInit';
} else { // Otherwise do things the old fashioned way
	$wgHooks['ParserAfterTidy'][]='efTimelineAfterTidy';
	$wgExtensionFunctions[] = 'efTimelineParserInit';
}
 
function efTimelineParserInit() {
	global $wgParser;
	$wgParser->setHook( 'timeline', 'efTimelineRender' );
	return true;
}
 
 
function efTimelineRender( $input, $args, $parser ) {
 
	// Extract parameters from global variables
	global $wgTimelineFieldSeparator;
	global $wgTimelineDateSeparator;
	global $wgTimelineLineSeparator;
 
	// Parse tag arguments
	$title=$args['title'];
	$footer=$args['footer'];
 
	// Caculate time range
	$lines=split($wgTimelineLineSeparator,trim($input));
	// Get the first and last years in the timeline
	$startYear=9999; // Should work for a while
	$endYear=-1;
	foreach ( $lines as $val )
	{
		$year=split($wgTimelineDateSeparator,trim($val),2);
		if ( (int)($year[0])<$startYear ){$startYear=(int)($year[0]);}
		$tmp=split($wgTimelineFieldSeparator,trim($val));
		$year=split($wgTimelineDateSeparator,trim($tmp[1]),2);
		if ( (int)($year[0])>$endYear ){$endYear=(int)($year[0]);}
	}
	// Find the number of months for each year
	$curStartMonth=13;
	$curEndMonth=0;
	foreach ( $lines as $val )
	{
		$tmp=split($wgTimelineFieldSeparator,trim($val));
		list($eventStartYear,$eventStartMonth)=split($wgTimelineDateSeparator,$tmp[0]);
		list($eventEndYear,$eventEndMonth)=split($wgTimelineDateSeparator,$tmp[1]);
		$eventStartYear=((int)$eventStartYear);
		$eventEndYear=((int)$eventEndYear);
		$eventStartMonth=(int)$eventStartMonth;
		$eventEndMonth=(int)$eventEndMonth;
		if ( $eventStartYear==$startYear )
		{
			if ( $eventStartMonth<$curStartMonth )
			{
				$curStartMonth=$eventStartMonth;
			}
		}
		if ( $eventEndYear==$endYear )
		{
			if ( $eventEndMonth>$curEndMonth )
			{
				$curEndMonth=$eventEndMonth;
			}
		}
	}
	$nMonths[]=12-$curStartMonth+1;
	for ( $year=$startYear+1 ; $year<$endYear ; $year++)
	{
		$nMonths[]=12;
	}
	$nMonths[]=$curEndMonth;
	// Number of years to display
	$nYears=$endYear-$startYear+1;
	// $nColumns contains the total number of months over the time range
	$nColumns=array_sum($nMonths);
	for ( $year=$startYear ; $year<=$endYear ; $year++ )
	{
		if ( $year==$startYear )
		{
			$startMonth[]=12-$nMonths[$year-$startYear];
		}
		else
		{
			$startMonth[]=0;
		}
		if ( $year==$endYear )
		{
			$endMonth[]=$nMonths[$year-$startYear];
		}
		else
		{
			$endMonth[]=12;
		}
	}
 
	// Additional parameters
	// List of the months as they should be displayed in the timeline cell
	$monthList=array( "J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D");
	//$monthList=array( "January" => "J", "February" => "F", "March" => "M", "April" => "A", "May" => "M", "June" => "J", "July" => "J", "August" => "A", "September" => "S", "October" => "O", "November" => "N", "December" => "D");
	//$monthList=array( "January" , "February" , "March" , "April" , "May" , "June" , "July" , "August" , "September" , "October" , "November" , "December" );	
 
	// Create the timeline: $timeline_str will contain the html code for the table
 
	// Start the table
	$timeline_str='<table class=tl_table>';
 
	// Header: title line
	$timeline_str=$timeline_str . '<thead class=tl_header>';
	$timeline_str=$timeline_str . '<tr>';
	$timeline_str=$timeline_str . '<th colspan="' . $nColumns . '" class=tl_title>';
	$timeline_str=$timeline_str . htmlspecialchars($title);
	$timeline_str=$timeline_str . '</th>';
	$timeline_str=$timeline_str . '</tr>';
	// Header: Years timeline
	$timeline_str=$timeline_str . '<tr>';
	for ( $year=$startYear ; $year<=$endYear ; $year++ )
	{
		$timeline_str=$timeline_str . '<th colspan="' . $nMonths[$year-$startYear] . '" class=tl_years>';
		$timeline_str=$timeline_str . $year;
		$timeline_str=$timeline_str . '</th>';
	}
	$timeline_str=$timeline_str . '</tr>';
	$timeline_str=$timeline_str . '<tr>';
	// Header: Months
	for ( $year=$startYear ; $year<=$endYear ; $year++ )
	{
		for ( $month=$startMonth[$year-$startYear] ; $month<$endMonth[$year-$startYear] ; $month++ )
		{
			$timeline_str=$timeline_str . '<th class=tl_months>';
			$timeline_str=$timeline_str . $monthList[$month];
			$timeline_str=$timeline_str . '</th>';
		}
	}
	$timeline_str=$timeline_str . '</tr>';
	$timeline_str=$timeline_str . '</thead>';
 
	// Footer
	$timeline_str=$timeline_str . '<tfoot class=tl_footer><tr><td colspan="' . $nColumns . '" class=tl_foot>';
	$timeline_str=$timeline_str . htmlspecialchars($footer);
	$timeline_str=$timeline_str . '</td></tr></tfoot>';
 
	// Body: Events (display one event per row)
	$timeline_str=$timeline_str . '<tbody class=tl_body>';
	foreach ( $lines as $val )
	{
		// Parse the event dates and content
		list($eventStartDate,$eventEndDate,$text,$comment,$cssStyle)=split($wgTimelineFieldSeparator,$val);
		list($eventStartYear,$eventStartMonth)=split($wgTimelineDateSeparator,$eventStartDate);
		list($eventEndYear,$eventEndMonth)=split($wgTimelineDateSeparator,$eventEndDate);
		$eventStartYear=(int)$eventStartYear;
		$eventStartMonth=(int)$eventStartMonth;
		$eventEndYear=(int)$eventEndYear;
		$eventEndMonth=(int)$eventEndMonth;
 
		// Find the number of months between the first month of the timeline and the first month of the event
		$nPreviousMonths=array_sum(array_slice($nMonths,0,$eventStartYear-$startYear));
		$nPreviousMonths=$nPreviousMonths+$eventStartMonth-$startMonth[$eventStartYear-$startYear]-1;
 
		// Find the length of the event (in months)
		$nEventMonths=12-$eventStartMonth+1;
		$nEventMonths=$nEventMonths+$eventEndMonth;
		$nEventMonths=$nEventMonths+12*($eventEndYear-$eventStartYear-1);
 
		// Define the number of months between the end of the event and the end of the timeline
		$nRemainingMonths=$nColumns-$nPreviousMonths-$nEventMonths;
 
		// Merge the months before the event into a 'freetime' cell
		$timeline_str=$timeline_str . '<tr>';
		if ( $nPreviousMonths > 0 )
		{
			$timeline_str=$timeline_str . '<td colspan="' . $nPreviousMonths . '" class=tl_freetime></td>';
		}
 
		// Create the event cell
		$timeline_str=$timeline_str . '<td colspan="' . $nEventMonths . '" class=tl_event ';
		if ( strcmp(trim($cssStyle),"") )
		{
			$timeline_str=$timeline_str . 'style="' . htmlspecialchars($cssStyle) . '"';
		}
		$timeline_str=$timeline_str . '>'; 
		//$timeline_str=$timeline_str . htmlspecialchars($text);
		$timeline_str=$timeline_str . $parser->recursiveTagParse($text);
		if ( strcmp(trim($comment),"") )
		{
			//$timeline_str=$timeline_str . '<br />(' . htmlspecialchars($comment) . ')';
			$timeline_str=$timeline_str . '<br />(' . $parser->recursiveTagParse($comment) . ')';
		}
		$timeline_str=$timeline_str . '</td>';
 
		// Merge the months after the event into a 'freetime' cell
		if ( $nRemainingMonths > 0 )
		{
			$timeline_str=$timeline_str . '<td colspan="' . $nRemainingMonths . '" class=tl_freetime></td>';
		}
		$timeline_str=$timeline_str . '</tr>';		
	}
	$timeline_str=$timeline_str . '</tbody>';
 
	// Finish table
	$timeline_str=$timeline_str . '</table><br />';
 
	// 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 efTimelineAfterTidy(&$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
/*
	/ CSS file for table and define classes for each type of cell -> common.css
	- Finish syntax YYYY-MM|YYYY-MM|text|color|bgcolor|comment
	- Options
	- Check execution time
	- Clear code
	- Check inputs (date order, etc.)
*/
?>