<?php
/********************************
OSBib:
A collection of PHP classes to create and manage bibliographic formatting for OS bibliography software 
using the OSBib standard.

Released through http://bibliophile.sourceforge.net under the GPL licence.
Do whatever you like with this -- some credit to the author(s) would be appreciated.

If you make improvements, please consider contacting the administrators at bibliophile.sourceforge.net 
so that your improvements can be added to the release package.

Mark Grimshaw 2006
http://bibliophile.sourceforge.net
********************************/

/** Description of class BIBFORMAT
* Format a bibliographic resource for output.
* 
* @author	Mark Grimshaw
* @version	1
*/
class BIBFORMAT
{
/**
* $dir is the path to STYLEMAP.php etc.
*/
	function BIBFORMAT($dir = FALSE, $bibtex = FALSE, $preview = FALSE)
	{
//05/05/2005 G.GARDEY: add a last "/" to $stylePath if not present.
		$this->preview = $preview;
		if(!$this->preview) // Not javascript preview
		{
			$dir = trim($dir);
			if(!$dir){
				$this->dir = dirname(__FILE__) . "/";
			}
			else{
				$this->dir = $dir;
				if($dir[strlen($dir)-1] != "/"){
					$this->dir .= "/";
				}
			}
		}
		else if(!$dir) // preview
			$this->dir = dirname(__FILE__) . "/";
		$this->bibtex = $bibtex;
		if($this->bibtex)
		{
			include_once(OSBIB__STYLEMAPBIBTEX);
			$this->styleMap = new STYLEMAPBIBTEX();
		}
		else
		{
			include_once(OSBIB__STYLEMAP);
			$this->styleMap = new STYLEMAP();
		}
		include_once(OSBIB__UTF8);
		$this->utf8 = new UTF8();
/**
* Highlight preg pattern and CSS class for HTML display
*/
		$this->patterns = FALSE;
		$this->patternHighlight = FALSE;
/**
* Output medium:
* 'html', 'rtf', or 'plain'
*/
		$this->output = 'html'; // default
		$this->previousCreator = '';
/**
* Switch editor and author positions in the style definition for a book in which there are only editors
*/
		$this->editorSwitch = $this->dateMonthNoDay = FALSE;
		$this->creators = array('creator1', 'creator2', 'creator3', 'creator4', 'creator5');
// Some styles require different templates and formatting of creator names for a citation in a footnote as opposed to a full bibliography.  Setting this to TRUE (set 
// externally in CITEFORMAT) loads a different set of templates and settings for footnotes.  The default FALSE is for full bibliography.
		$this->citationFootnote = FALSE;
		$this->footnotePages = FALSE;
		$this->footnoteType = FALSE;
		$this->partialDone = FALSE;
/**
 *  Convert the bibTeX special characters to produce utf8
 *  Defaut: 'FALSE', we assume that the entries are already clean
 */
		$this->cleanEntry = FALSE;
// WIKINDX-specific
		$this->wikindx = FALSE;
	}
/**
* Read the chosen bibliographic style and create arrays based on resource type.
* 
* @author	Mark Grimshaw
* @version	1
*
* @param	$stylePath	The path where the styles are.
* @param	$style		The requested bibliographic output style.
* @return	BOOLEAN
*/
	function loadStyle($stylePath, $style)
	{
        //05/05/2005 G.GARDEY: add a last "/" to $stylePath if not present.
		$stylePath = trim($stylePath);
		if($stylePath[strlen($stylePath)-1] != "/"){
			$stylePath .= "/";
		}
		$uc = $stylePath . strtolower($style) . "/" . strtolower($style) . ".xml";
		$lc = $stylePath . strtolower($style) . "/" . strtoupper($style) . ".xml";
		$styleFile = file_exists($uc) ? $uc : $lc;
		if(!$fh = fopen($styleFile, "r"))
			return array(FALSE, FALSE, FALSE, FALSE, FALSE);
		include_once(OSBIB__PARSEXML);
		$parseXML = new PARSEXML($this);
		list($info, $citation, $footnote, $common, $types) = $parseXML->extractEntries($fh);
		fclose($fh);
		return array($info, $citation, $footnote, $common, $types);
	}
/**
* Transform the raw data from the XML file into usable arrays and load some data arrays
*
* @author	Mark Grimshaw
* @version	1
*
* @param	$common		Array of global formatting data
* @param	$types		Array of style definitions for each resource type
* @param	$footnote		Array of style definitions for footnote creators
*/
	function getStyle($common, $types, $footnote)
	{
		$this->commonToArray($common);
		$this->footnoteToArray($footnote);
		$this->typesToArray($types);
/**
* Load localisations etc.
*/
		$this->loadArrays();
	}
/**
* Reformat the array representation of common styling into a more useable format.
* 'common' styling refers to formatting that is common to all resource types such as creator formatting, title 
* capitalization etc.
*
* @author	Mark Grimshaw
* @version	1
*
* @param	$common		nodal array representation of XML data
* @return	flattened array representation for easier use.
*/
	function commonToArray($common)
	{
		foreach($common as $array)
		{
			if(array_key_exists('_NAME', $array) && array_key_exists('_DATA', $array))
				$this->style[$array['_NAME']] = $array['_DATA'];
		}
	}
/**
* Reformat the array representation of resource types into arrays based on the type.
*
* @param	$types		nodal array representation of XML data
*/
	function typesToArray($types)
	{
		foreach($types as $resourceArray)
		{
/**
* The resource type which will be our array name
*/
			$type = $resourceArray['_ATTRIBUTES']['name'];
			$typeTemplateSet = $type . 'TemplateSet';
			$this->rewriteCreatorsToArray($type, $resourceArray);
			$styleDefinition = $resourceArray['_ELEMENTS'];
			foreach($styleDefinition as $array)
			{
				if(array_key_exists('_NAME', $array) && array_key_exists('_DATA', $array) 
					 && array_key_exists('_ELEMENTS', $array))
				{
					if($array['_NAME'] == 'fallbackstyle')
					{
						$this->fallback[$type] = $array['_DATA'];
						continue;
					}
					if($array['_NAME'] == 'ultimate')
					{
						$this->{$type}['ultimate'] = $array['_DATA'];
						continue;
					}
					if($array['_NAME'] == 'preliminaryText')
					{
						$this->{$type}['preliminaryText'] = $array['_DATA'];
						continue;
					}
					if(($array['_NAME'] == 'partial') && $array['_DATA'])
					{
						include_once(OSBIB__PARSESTYLE);
						$parsestyle = new PARSESTYLE();
						$this->{$type}['partial'] = $parsestyle->parseStringToArray($type, 
							$array['_DATA'], $this->styleMap);
						continue;
					}
					if($array['_NAME'] == 'partialReplace')
					{
						$this->{$type}['partialReplace'] = $array['_DATA'];
						continue;
					}
					foreach($array['_ELEMENTS'] as $elements)
					{
						$data = $elements['_DATA'];
						if($array['_NAME'] == 'independent')
						{
							$split = split("_", $elements['_NAME']);
							$this->{$type}[$array['_NAME']][$split[1]] = $data;
						}
						else
							$this->{$type}[$array['_NAME']][$elements['_NAME']] = $data;
						$this->$typeTemplateSet = TRUE;
					}
				}
			}
/**
* Backup each $this->$type array.  If we need to switch editors or date fields, it's faster to restore each 
* $this->$type array from this backup than to reload the style file and parse it.
*/
			if(isset($this->$typeTemplateSet))
				$this->backup[$type] = $this->$type;
		}
	}
/**
* Reformat the array representation of footnote resource styling into a more useable format.
*
* @author	Mark Grimshaw
* @version	1
*
* @param	$footnote		nodal array representation of XML data
* @return	flattened array representation for easier use.
*/
	function footnoteToArray($footnote)
	{
		foreach($footnote as $array)
		{
			if(array_key_exists('_NAME', $array) && array_key_exists('_DATA', $array))
			{
				if($array['_NAME'] != 'resource')
					$this->footnoteStyle[$array['_NAME']] = $array['_DATA'];
				else if(array_key_exists('_ELEMENTS', $array) && !empty($array['_ELEMENTS']))
				{
					$footnoteType = "footnote_" . $array['_ATTRIBUTES']['name'];
					foreach($array['_ELEMENTS'] as $fArray)
					{
						if($fArray['_NAME'] == 'ultimate')
						{
							$this->{$footnoteType}['ultimate'] = $fArray['_DATA'];
							continue;
						}
						if($fArray['_NAME'] == 'preliminaryText')
						{
							$this->{$footnoteType}['preliminaryText'] = $fArray['_DATA'];
							continue;
						}
						foreach($fArray['_ELEMENTS'] as $elements)
						{
							if($fArray['_NAME'] == 'independent')
							{
								$split = split("_", $elements['_NAME']);
								$this->{$footnoteType}[$fArray['_NAME']][$split[1]] = $elements['_DATA'];
							}
							else
								$this->{$footnoteType}[$fArray['_NAME']][$elements['_NAME']] = 
								$elements['_DATA'];
						}
					}
				}
			}
		}
	}
/**
* Add resource-specific rewrite creator fields to $this->$type array
*
* @author	Mark Grimshaw
* @version	1
*/
	function rewriteCreatorsToArray($type, $array)
	{
		foreach($this->creators as $creatorField)
		{
			$name = $creatorField . "_firstString";
			if(array_key_exists($name, $array['_ATTRIBUTES']))
				$this->{$type}[$name] = $array['_ATTRIBUTES'][$name];
			$name = $creatorField . "_firstString_before";
			if(array_key_exists($name, $array['_ATTRIBUTES']))
				$this->{$type}[$name] = $array['_ATTRIBUTES'][$name];
			$name = $creatorField . "_remainderString";
			if(array_key_exists($name, $array['_ATTRIBUTES']))
				$this->{$type}[$name] = $array['_ATTRIBUTES'][$name];
			$name = $creatorField . "_remainderString_before";
			if(array_key_exists($name, $array['_ATTRIBUTES']))
				$this->{$type}[$name] = $array['_ATTRIBUTES'][$name];
			$name = $creatorField . "_remainderString_each";
			if(array_key_exists($name, $array['_ATTRIBUTES']))
				$this->{$type}[$name] = $array['_ATTRIBUTES'][$name];
		}
	}
/**
* Restore each $this->type array from $this->backup
*
* @author	Mark Grimshaw
* @version	1
*/
	function restoreTypes()
	{
		foreach($this->backup as $type => $array)
			$this->$type = $array;
	}
/**
* Perform pre-processing on the raw SQL array
*
* @author	Mark Grimshaw
* @version	1
*
* @param	$type	The resource type
* @param	$row	Associate array of raw SQL data
* @return	$row	Processed row of raw SQL data
*/
	function preProcess($ttype, $row)
	{
/**
* Ensure that $this->item is empty for each resource!!!!!!!!!!
*/
		$this->item = array();
// Map this system's resource type to OSBib's resource type
// If FALSE, STYLEMAP does not have any array defined for this type so return doing nothing
		if(($this->type = array_search($ttype, $this->styleMap->types)) === FALSE) {
		    echo "$ttype : cannot find type<br>";
			//return $row;
			return FALSE;
		}
		$type=$this->type;
		$typeTemplateSet = $type . 'TemplateSet';
		if($this->bibtex && array_key_exists('author', $row))
		{
			$row['creator1'] = $row['author'];
			unset($row['author']);
		}
		if($this->bibtex && array_key_exists('editor', $row))
		{
			$row['creator2'] = $row['editor'];
			unset($row['editor']);
		}
/**
* Set any author/editor re-ordering for book and book_article type.
*/
		if(!$this->preview && (($type == 'book') || ($type == 'book_article')) && 
			array_key_exists('creator2', $row) && $row['creator2'] && 
			array_key_exists('creator1', $row) && !$row['creator1'] && $this->style['editorSwitch'] &&
			array_key_exists('author', $this->$type))
		{
			include_once(OSBIB__PARSESTYLE);
			$parsestyle = new PARSESTYLE();
			$row['creator1'] = $row['creator2'];
			$row['creator2'] = FALSE;
			$editorArray = $parsestyle->parseStringToArray($type, $this->style['editorSwitchIfYes'], 
				$this->styleMap);
			if(!empty($editorArray) && array_key_exists('editor', $editorArray))
			{
				$this->{$type}['author'] = $editorArray['editor'];
 				unset($this->{$type}['editor']);
				$this->editorSwitch = TRUE;
			}
		}
		if($this->style['dateMonthNoDay'] && array_key_exists('date', $this->styleMap->$type) && 
			array_key_exists('dateMonthNoDayString', $this->style) && $this->style['dateMonthNoDayString'])
		{
			include_once(OSBIB__PARSESTYLE);
			$parsestyle = new PARSESTYLE();
			$this->dateArray = $parsestyle->parseStringToArray($type, $this->style['dateMonthNoDayString'], 
				$this->styleMap, TRUE);
			$this->dateMonthNoDay = TRUE;
		}
/**
* If $row comes in in BibTeX format, process and add items to $this->item
*/
		if($this->bibtex) 
		  {
		    if(!$this->type)
		      {
			list($type, $row) = $this->preProcessBibtex($row, $type);
		      } else 
			list($type, $row) = $this->preProcessBibtex($row, $this->type);
		  }
/**
* Ensure that for theses types, the first letter of type and label are capitalized (e.g. 'Master's Thesis').
*/
		if($type == 'thesis')
		{
			if(($key = array_search('type', $this->styleMap->$type)) !== FALSE)
			{
				if(isset($row[$key]))
					$row[$key] = ucfirst($row[$key]);
			}
			if(($key = array_search('label', $this->styleMap->$type)) !== FALSE)
			{
				if(isset($row[$key]))
					$row[$key] = ucfirst($row[$key]);
			}
		}
/**
* Set to catch-all generic style.  For all keys except named database fields, creator1 and year1, 
* we only print if the value in $this->styleMap matches the value in 
* $this->styleMap->generic for each key.
*/
		if($this->citationFootnote) // using footnote template
		{
			$footnoteType = 'footnote_' . $type;
			if(isset($this->$footnoteType)) // footnote template for this resource exists
			{
				$this->footnoteType = $footnoteType;
				$this->footnoteTypeArray[$type] = $footnoteType;
			}
			else 
			{
				$footnoteType = 'footnote_' . $this->fallback[$type];
				if(isset($this->$footnoteType)) // fallback footnote template exists
				{
					$this->footnoteType = $footnoteType;
					$this->footnoteTypeArray[$type] = $footnoteType;
				}
				else if(!isset($this->$typeTemplateSet)) // use fallback bibliography template
				{
					$fallback = $this->fallback[$type];
					$this->footnoteTypeArray[$type] = $fallback;
					$type = $fallback;
				}
				// else, we're using the bibliography template for this resource type
				else
					$this->footnoteTypeArray[$type] = $type;
			}
		}
		else
		{
			if(!isset($this->$typeTemplateSet))
			{
				$fallback = $this->fallback[$type];
				$type = $fallback;
			}
		}
		$this->type = $type;
/**
* Add BibTeX entry to $this->item
*/
		if($this->bibtex)
		{
			foreach($row as $field => $value)
			{
				if(array_key_exists($field, $this->styleMap->$type) && 
					!array_key_exists($this->styleMap->{$type}[$field], $this->item))
					$this->addItem($row[$field], $field);
			}
		}
		return $row;
	}
/**
* Preprocess BibTeX-type entries
* @author Mark Grimshaw
* @version 1
*
* @param assoc. array of elements for one bibtex entry
* @param string resource type
* @return string resource type
* @return array resource assoc. array of elements for one bibtex entry
*/
	function preProcessBibtex(&$row, $type)
	{
//05/05/2005 G.GARDEY: change bibtexParse name.
/**
* This set of includes is for the OSBib public release and should be uncommented for that and
* the WIKINDX-specific includes below commented out!
*/
		//include_once(OSBIB__PARSECREATORS); //well - they are autoloaded in code igniter / aigaion, because they were originally not loaded with exactly this pathname...
		$parseCreator = new PARSECREATORS();
		$parseCreator->separateInitials = TRUE;
		//include_once(OSBIB__PARSEMONTH);
		$parseDate = new PARSEMONTH();
		//include_once(OSBIB__PARSEPAGE);
		$parsePages = new PARSEPAGE();

// WIKINDX naming of above files
/*
		include_once($this->bibtexParsePath . "/BIBTEXCREATORPARSE.php");
		$parseCreator = new BIBTEXCREATORPARSE();
		include_once($this->bibtexParsePath . "/BIBTEXMONTHPARSE.php");
		$parseDate = new BIBTEXMONTHPARSE();
		include_once($this->bibtexParsePath . "/BIBTEXPAGEPARSE.php");
		$parsePages = new BIBTEXPAGEPARSE();
*/
// Added by Christophe Ambroise: convert the bibtex entry to utf8 (for storage or printing)
		if($this->cleanEntry)
			$row = $this->convertEntry($row);
/**
* Bibtex-specific types not defined in STYLEMAPBIBTEX
*/
		if(!$this->type)
		{
			if($type == 'mastersthesis')
			{
				$type = 'thesis';
				$row['type'] = "Master's Dissertation";
			}
			if($type == 'phdthesis')
			{
				$type = 'thesis';
				$row['type'] = "PhD Thesis";
			}
			else if($type == 'booklet')
				$type = 'miscellaneous';
			else if($type == 'conference')
				$type = 'proceedings_article';
			else if($type == 'incollection')
				$type = 'book_article';
			else if($type == 'manual')
				$type = 'report';
		}
/**
* 'article' could be journal, newspaper or magazine article
*/
		else if($type == 'journal_article')
		{
			if(array_key_exists('month', $row) && array_key_exists('date', $this->styleMap->$type))
			{
				list($startMonth, $startDay, $endMonth, $endDay) = $parseDate->init($row['month']);
				if($startDay)
					$type = 'newspaper_article';
				else if($startMonth)
					$type = 'magazine_article';
				$this->formatDate($startDay, $startMonth, $endDay, $endMonth);
			}
			else
				$type = 'journal_article';
		}
/**
* Is this a web article?
*/
		else if(($type == 'miscellaneous') && array_key_exists('howpublished', $row))
		{
			if(preg_match("#^\\\url{(.*://.*)}#", $row['howpublished'], $match))
			{
				$row['URL'] = $match[1];
				$type = 'web_article';
			}
		}
		$this->type = $type;
		if(array_key_exists('creator1', $row) && $row['creator1'] && 
			array_key_exists('creator1', $this->styleMap->$type))
		{
			$creators = $parseCreator->parse($row['creator1']);
			foreach($creators as $cArray)
			{
			    $cArray['initials'] = str_replace('.', '', trim($cArray['initials']));
			    $temp[] = $cArray;//changed because bibparse now directly returns this expected format
//				if(trim($cArray[3])) // 'Jr.'
//					$cArray[3] = ' ' . trim($cArray[3]);
//				$temp[] = array(
//						'surname'	=>	trim($cArray[2]) . $cArray[3],
//						'firstname'	=>	trim($cArray[0]),
//						'initials'	=>	str_replace('.', '', trim($cArray[1])),
//						'prefix'	=>	trim($cArray[4]),
//					);
			}
			$this->formatNames($temp, 'creator1');
			unset($temp);
		}
		if(array_key_exists('creator2', $row) && $row['creator2'] && 
			array_key_exists('creator2', $this->styleMap->$type))
		{
			$creators = $parseCreator->parse($row['creator2']);
			foreach($creators as $cArray)
			{
			    $cArray['initials'] = str_replace('.', '', trim($cArray['initials']));
			    $temp[] = $cArray; //changed because bibparse now directly returns this expected format
//				if(trim($cArray[3])) // 'Jr.'
//					$cArray[3] = ' ' . trim($cArray[3]);
//				$temp[] = array(
//						'surname'	=>	trim($cArray[2]) . $cArray[3],
//						'firstname'	=>	trim($cArray[0]),
//						'initials'	=>	str_replace('.', '', trim($cArray[1])),
//						'prefix'	=>	trim($cArray[4]),
//					);
			}
			$this->formatNames($temp, 'creator2');
		}
		if(array_key_exists('pages', $row) && array_key_exists('pages', $this->styleMap->$type))
		{
			list($start, $end) = $parsePages->init($row['pages']);
			$this->formatPages(trim($start), trim($end));
		}
		$this->formatTitle($row['title'], "{", "}");
		return array($type, $row);
	}
/**
* Find alternate fields dependent on previous or following fields and deal with accordingly.
* 
* @author	Mark Grimshaw
* @version	1
*/
	function findAlternates($typeArray)
	{
		foreach($typeArray as $field => $array)
		{
			if(is_array($array)&& array_key_exists($field, $this->item))
			{
				if(array_key_exists('alternatePreFirst', $array) || 
					array_key_exists('alternatePreSecond', $array))
				{
					if(!$this->item[$field])
					{
						if(array_key_exists($array['alternatePreFirst'], $this->item))
							unset($this->item[$array['alternatePreFirst']]);
					}
					else
					{
						if(array_key_exists($array['alternatePreSecond'], $this->item))
							unset($this->item[$array['alternatePreSecond']]);
					}
				}
				if(array_key_exists('alternatePostFirst', $array) || 
					array_key_exists('alternatePostSecond', $array))
				{
					if(!$this->item[$field])
					{
						if(array_key_exists($array['alternatePostFirst'], $this->item))
							unset($this->item[$array['alternatePostFirst']]);
					}
					else
					{
						if(array_key_exists($array['alternatePostSecond'], $this->item))
							unset($this->item[$array['alternatePostSecond']]);
					}
				}
			}
		}
	}
/**
* Check that the first field of $this->$type exists in $this->item and has a value.  If it does not and $this->$type has a partial template array, rearrange $this->$type
* 
* @author	Mark Grimshaw
* @version	1
*
* @param	$type	Resource type
*/
	function checkPartial($type)
	{
//print "TYPE: "; print_r($this->$type); print "<P>";
//print "ITEM: "; print_r($this->item); print "<P>";
		$this->partialDone = FALSE;
		if(!array_key_exists('partial', $this->$type))
			return; // nothing to do
		$typeKeys = array_keys($this->$type);
		if(array_key_exists($typeKeys[0], $this->item) && $this->item[$typeKeys[0]])
			return;	// item key exists -- nothing to do
// Replace the whole template with the partial template?
		if($this->{$type}['partialReplace'])
		{
			$partial = $this->{$type}['partial'];
			$this->$type = $partial;
// If independent keys exists, because we have removed the first key from $this->$type, we must decrement the keys in independent
			if(array_key_exists('independent', $this->$type) && !empty($this->{$type}['independent']))
			{
				foreach($this->{$type}['independent'] as $key => $value)
					$tempInd[$key - 1] = $value;
				$this->{$type}['independent'] = $tempInd;
			}
//print "TYPE: "; print_r($this->$type); print "<P>";
			return;
		}
// At this point need to rearrange
		$oldType = $this->$type;
// Remove missing first item
		array_shift($oldType);
		foreach($this->{$type}['partial'] as $key => $value)
			unset($oldType[$key]);
		$this->$type = array_merge($this->{$type}['partial'], $oldType);
// If independent keys exists, because we have removed the first key from $this->$type, we must decrement the keys in independent
		if(array_key_exists('independent', $this->$type) && !empty($this->{$type}['independent']))
		{
			foreach($this->{$type}['independent'] as $key => $value)
				$tempInd[$key - 1] = $value;
			$this->{$type}['independent'] = $tempInd;
		}
		$this->partialDone = TRUE;
//print "TYPE: "; print_r($this->$type); print "<P>";
//print "ITEM: "; print_r($this->item); print "<P>";
	}
/**
* Map the $item array against the style array ($this->$type) for this resource type and produce a string ready to be 
* formatted for bold, italics etc.
* 
* @author	Mark Grimshaw
* @version	1
*
* @param	$template	If called from CITEFORMAT, this is the array of template elements.
* @return	string ready for printing to the output medium.
*/
	function map($template = FALSE)
	{
/**
* Output medium:
* 'html', 'rtf', or 'plain'
*/
		include_once(OSBIB__EXPORTFILTER);
		$this->export = new EXPORTFILTER($this, $this->output);
// Don't think $template is used anymore
		if($template)
		{
			$this->citation = $template;
			$this->type = 'citation';
		}
		$type = $pluralType = $this->type;
// Check for partial templates
		if(!$this->footnoteType)
			$this->checkPartial($type);
		else
		{
			$type = $this->footnoteType;
			$this->footnoteType = FALSE;
		}
		$this->findAlternates($this->$type);
		$ultimate = $preliminary = '';
		$index = 0;
		$previousFieldExists = $nextFieldExists = TRUE;
		if(array_key_exists('independent', $this->$type))
			$independent = $this->{$type}['independent'];
/**
* For dependency on next field, we must grab array keys of $this->$type, shift the first element then, in the loop, 
* check each element exists in $item.  If it doesn't, $nextFieldExists is set to FALSE
*/
		$checkPost = array_keys($this->$type);
		array_shift($checkPost);
		$lastFieldKey = FALSE;
// Add or replace pages field if this process is called from CITEFORMAT for footnotes where $this->footnotePages are the formatted citation pages.
		if($this->footnotePages)
			$this->item['pages'] = $this->footnotePages;
		foreach($this->$type as $key => $value)
		{
			if(($key == 'partial') || ($key == 'partialReplace'))
				continue;
			if($key == 'ultimate')
			{
				$ultimate = $value;
				continue;
			}
			if($key == 'preliminaryText')
			{
				$preliminary = $value;
				continue;
			}
			if(!array_key_exists($key, $this->item) || !$this->item[$key])
			{
				$keyNotExists[] = $index;
				$index++;
				array_shift($checkPost);
				$previousFieldExists = FALSE;
				continue;
			}
			$checkPostShift = array_shift($checkPost);
			if(!array_key_exists($checkPostShift, $this->item) || !$this->item[$checkPostShift])
				$nextFieldExists = FALSE;
			$pre = array_key_exists('pre', $value) ? $value['pre'] : '';
			$post = array_key_exists('post', $value) ? $value['post'] : '';
/**
* Deal with __DEPENDENT_ON_PREVIOUS_FIELD__ for characters dependent on previous field's existence and 
* __DEPENDENT_ON_NEXT_FIELD__ for characters dependent on the next field's existence
*/
			if($previousFieldExists && array_key_exists('dependentPre', $value))
				$pre = preg_replace("/__DEPENDENT_ON_PREVIOUS_FIELD__/", 
					$value['dependentPre'], $pre);
			else if(array_key_exists('dependentPreAlternative', $value))
				$pre = preg_replace("/__DEPENDENT_ON_PREVIOUS_FIELD__/", 
					$value['dependentPreAlternative'], $pre);
			else
				$pre = preg_replace("/__DEPENDENT_ON_PREVIOUS_FIELD__/", '', $pre);
			if($nextFieldExists && array_key_exists('dependentPost', $value))
				$post = str_replace("__DEPENDENT_ON_NEXT_FIELD__", 
					$value['dependentPost'], $post);
			else if(array_key_exists('dependentPostAlternative', $value))
				$post = preg_replace("/__DEPENDENT_ON_NEXT_FIELD__/", 
					$value['dependentPostAlternative'], $post);
			else
				$post = preg_replace("/__DEPENDENT_ON_NEXT_FIELD__/", '', $post);
/**
* Deal with __SINGULAR_PLURAL__ for creator lists and pages
*/			if($styleKey = array_search($key, $this->styleMap->$pluralType))
				$pluralKey = $styleKey . "_plural";
// For use with generic footnote templates which uses generic 'creator' field
			else
				$pluralKey = "creator_plural";
			if(isset($this->$pluralKey) && $this->$pluralKey) // plural alternative for this key
			{
				$pre = array_key_exists('plural', $value) ? 
					preg_replace("/__SINGULAR_PLURAL__/", $value['plural'], $pre) : $pre;
				$post = array_key_exists('plural', $value) ? 
					preg_replace("/__SINGULAR_PLURAL__/", $value['plural'], $post) : $post;
			}
			else if(isset($this->$pluralKey)) // singular alternative for this key
			{
				$pre = array_key_exists('singular', $value) ? 
					preg_replace("/__SINGULAR_PLURAL__/", $value['singular'], $pre) : $pre;
				$post = array_key_exists('singular', $value) ? 
					preg_replace("/__SINGULAR_PLURAL__/", $value['singular'], $post) : $post;
			}
// Deal with en dash characters in pages
			if($key == 'pages')
				$this->item[$key] = $this->export->format($this->item[$key]);
/**
* Strip backticks used in template
*/
			$pre = str_replace("`", '', $pre);
			$post = str_replace("`", '', $post);
/**
* Make sure we don't have multiple punctuation characters after a field
*/			$lastPre = substr($post, -1);
			$firstItem = substr($this->item[$key], 0, 1);
			if($firstItem === $lastPre)
				$this->item[$key] = substr($this->item[$key], 1);
// Match last character of this field with $post
			if($post && preg_match("/[.,;:?!]$/", $this->item[$key]) &&
				preg_match("/^(\[.*?[\]]+)*([.,;:?!])|^([.,;:?!])/", $post, $capture, PREG_OFFSET_CAPTURE))
// There is punctuation in post either immediately following BBCode formatting or at the start of the string.
// The offset for the punctuation character in $post is given at $capture[2][1]
				$post = substr_replace($post, '', $capture[2][1], 1);
// Match $itemArray[$lastFieldKey] with $pre
			if(($lastFieldKey !== FALSE) && $pre && preg_match("/^[.,;:?!]/", $pre) && 
				preg_match("/([.,;:?!])(\[.*?[\]]+)*$|([.,;:?!])$/", 
				$itemArray[$lastFieldKey], $capture, PREG_OFFSET_CAPTURE))
// There is punctuation in post either immediately following BBCode formatting or at the start of the string.
				$pre = substr_replace($pre, '', 0, 1);
			if($this->item[$key])
			{
				$itemArray[$index] = $pre . $this->item[$key] . $post;
				$lastFieldKey = $index;
			}
			$previousFieldExists = $nextFieldExists = TRUE;
			$index++;
		}
/**
* Check for independent characters.  These (should) come in pairs.
*/		if(isset($independent))
		{
			$independentKeys = array_keys($independent);
			while($independent)
			{
				$preAlternative = $postAlternative = FALSE;
				$startFound = $endFound = FALSE;
				$pre = array_shift($independent);
				$post = array_shift($independent);
				if(preg_match("/%(.*)%(.*)%|%(.*)%/U", $pre, $dependent))
				{
					if(sizeof($dependent) == 4)
						$pre = $dependent[3];
					else
					{
						$pre = $dependent[1];
						$preAlternative = $dependent[2];
					}
				}
				if(preg_match("/%(.*)%(.*)%|%(.*)%/U", $post, $dependent))
				{
					if(sizeof($dependent) == 4)
						$post = $dependent[3];
					else
					{
						$post = $dependent[1];
						$postAlternative = $dependent[2];
					}
				}
/**
* Strip backticks used in template
*/
				$preAlternative = str_replace("`", '', $preAlternative);
				$postAlternative = str_replace("`", '', $postAlternative);
				$firstKey = array_shift($independentKeys);
				$secondKey = array_shift($independentKeys);
				for($index = $firstKey; $index <= $secondKey; $index++)
				{
					if(array_key_exists($index, $itemArray))
					{
						$startFound = $index;
						break;
					}
				}
				for($index = $secondKey; $index >= $firstKey; $index--)
				{
					if(array_key_exists($index, $itemArray))
					{
						$endFound = $index;
						break;
					}
				}
				if(($startFound !== FALSE) && ($endFound !== FALSE)) // intervening fields found
				{
					$itemArray[$startFound] = $pre . $itemArray[$startFound];
					$itemArray[$endFound] = $itemArray[$endFound] . $post;
				}
				else // intervening fields not found - do we have an alternative?
				{
					if(array_key_exists($firstKey - 1, $itemArray) && $preAlternative)
						$itemArray[$firstKey - 1] .= $preAlternative;
					if(array_key_exists($secondKey + 1, $itemArray) && $postAlternative)
						$itemArray[$secondKey + 1] = $postAlternative . $itemArray[$secondKey + 1];
				}
			}
		}
		$pString = join('', $itemArray);
/**
* if last character is punctuation (which it may be with missing fields etc.), and $ultimate is also 
* punctuation, set $ultimate to empty string.
*/		if(isset($ultimate) && $ultimate)
		{
			$pString = trim($pString);
/**
* Don't do ';' in case last element is URL with &gt; ...!
*/
			if(preg_match("/^[.,:?!]/", $ultimate) && 
				preg_match("/([.,:?!])(\[.*?[\]]+)*$|([.,:?!])$/", $pString))
				$ultimate = '';
		}
// If $this->editorSwitch or $this->dateMonthNoDay, we have altered $this->$bibformat->$type so need to reload styles
		if(!$this->preview && ($this->editorSwitch || $this->dateMonthNoDay || $this->partialDone))
		{
			$this->restoreTypes();
			$this->editorSwitch = $this->dateMonthNoDay = FALSE;
		}
		return $this->export->format($preliminary . trim($pString) . $ultimate);
	}
/**
* Format creator name lists (authors, editors, etc.)
* 
* @author	Mark Grimshaw
* @version	1
* 
* @param	$creators	Multi-associative array of creator names e.g. this array might be of 
* the primary authors:
* <pre>
*	array([0] => array(['surname'] => 'Grimshaw', ['firstname'] => Mark, ['initials'] => 'N', ['prefix'] => ),
*	   [1] => array(['surname'] => 'Witt', ['firstname'] => Jan, ['initials'] => , ['prefix'] => 'de'))
* </pre>
* @param	$nameType	'creator1', 'creator2' etc.
* @param	$shortFootnote.  If TRUE, this is being used for just the primary creator names in a footnote style citation using Ibid, Idem, op cit. etc.
* @return	Optional if $nameType == 'citation': formatted string of all creator names in the input array.
*/
	function formatNames($creators, $nameType, $shortFootnote = FALSE)
	{
		$style = $this->citationFootnote ? $this->footnoteStyle : $this->style;
		$first = TRUE;
/**
* Citation creators
*/
		if($nameType == 'citation')
		{
			$limit = 'creatorListLimit';
			$moreThan = 'creatorListMore';
			$abbreviation = 'creatorListAbbreviation';
			$initialsStyle = 'creatorInitials';
			$firstNameInitial = 'creatorFirstName';
			$delimitTwo = 'twoCreatorsSep';
			$delimitFirstBetween = 'creatorSepFirstBetween';
			$delimitNextBetween = 'creatorSepNextBetween';
			$delimitLast = 'creatorSepNextLast';
			$uppercase = 'creatorUppercase';
			$italics = 'creatorListAbbreviationItalic';
		}
/**
* Primary creator
*/
		else if($nameType == 'creator1')
		{
			$limit = 'primaryCreatorListLimit';
			$moreThan = 'primaryCreatorListMore';
			$abbreviation = 'primaryCreatorListAbbreviation';
			$initialsStyle = 'primaryCreatorInitials';
			$firstNameInitial = 'primaryCreatorFirstName';
			$delimitTwo = 'primaryTwoCreatorsSep';
			$delimitFirstBetween = 'primaryCreatorSepFirstBetween';
			$delimitNextBetween = 'primaryCreatorSepNextBetween';
			$delimitLast = 'primaryCreatorSepNextLast';
			$uppercase = 'primaryCreatorUppercase';
			$italics = 'primaryCreatorListAbbreviationItalic';
		}
		else
		{
			$limit = 'otherCreatorListLimit';
			$moreThan = 'otherCreatorListMore';
			$abbreviation = 'otherCreatorListAbbreviation';
			$initialsStyle = 'otherCreatorInitials';
			$firstNameInitial = 'otherCreatorFirstName';
			$delimitTwo = 'otherTwoCreatorsSep';
			$delimitFirstBetween = 'otherCreatorSepFirstBetween';
			$delimitNextBetween = 'otherCreatorSepNextBetween';
			$delimitLast = 'otherCreatorSepNextLast';
			$uppercase = 'otherCreatorUppercase';
			$italics = 'otherCreatorListAbbreviationItalic';
		}
		$type = $this->type;
/**
* Set default plural behaviour for creator lists
*/
// For use with generic footnote templates which uses generic 'creator' field
		if($this->citationFootnote && ($nameType == 'creator1') && 
		($this->styleMap->{$type}[$nameType] != 'creator'))
					$pluralKey = "creator_plural";
		else
			$pluralKey = $nameType . "_plural";
		$this->$pluralKey = FALSE;
		$firstInList = TRUE;
		$rewriteCreatorBeforeDone = $rewriteCreatorFinal = FALSE;
		foreach($creators as $creator)
		{
			if(($nameType == 'citation') && $first)
				$nameStyle = 'creatorStyle';
			else if($nameType == 'citation')
				$nameStyle = 'creatorOtherStyle';
			else if(($nameType == 'creator1') && $first)
				$nameStyle = 'primaryCreatorFirstStyle';
			else if($nameType == 'creator1')
				$nameStyle = 'primaryCreatorOtherStyle';
			else if($first)
				$nameStyle = 'otherCreatorFirstStyle';
			else
				$nameStyle = 'otherCreatorOtherStyle';
			if(array_key_exists('id', $creator))
				$creatorIds[] = $creator['id'];
			$firstName = trim($this->checkInitials($creator, $style[$initialsStyle], 
				$style[$firstNameInitial]));
			$prefix = $creator['von'] ? trim($creator['von']) . ' ' : '';
			if($style[$nameStyle] == 0) // Joe Bloggs
			{
				$nameString = $firstName . ' ' . 
					$prefix . 
					$creator['surname'];
			}
			else if($style[$nameStyle] == 1) // Bloggs, Joe
			{
				$prefixDelimit = $firstName ? ', ' : '';
				$nameString = 
					$creator['von'] . ' ' . 
					$creator['surname'] . $prefixDelimit . 
					$firstName;
			}
			else if($style[$nameStyle] == 2) // Bloggs Joe
			{
				$nameString = 
					$creator['von'] . ' ' . 
					$creator['surname'] . ' ' . 
					$firstName;
			}
			else // Last name only
			{
				$nameString = 
					$creator['von'] . ' ' . 
					$creator['surname'];
			}
			if(isset($style[$uppercase]))
				$nameString = $this->utf8->utf8_strtoupper($nameString);
			$nameString = trim($nameString);
			if($firstInList)
			{
				$rewriteCreatorField = $nameType . "_firstString";
				$rewriteCreatorFieldBefore = $nameType . "_firstString_before";
			}
			else
			{
				$rewriteCreatorField = $nameType . "_remainderString";
				$rewriteCreatorFieldBefore = $nameType . "_remainderString_before";
				$rewriteCreatorFieldEach = $nameType . "_remainderString_each";
			}
			if(array_key_exists($rewriteCreatorField, $this->$type))
			{
				if($firstInList)
				{
					if(array_key_exists($rewriteCreatorFieldBefore, $this->$type))
						$nameString = $this->{$type}[$rewriteCreatorField] . $nameString;
					else
						$nameString .= $this->{$type}[$rewriteCreatorField];
					$firstInList = FALSE;
				}
				else if(array_key_exists($rewriteCreatorFieldEach, $this->$type))
				{
					if(array_key_exists($rewriteCreatorFieldBefore, $this->$type))
						$nameString = $this->{$type}[$rewriteCreatorField] . $nameString;
					else
						$nameString .= $this->{$type}[$rewriteCreatorField];
//print "$nameString<P>";
				}
				else
				{
					if(!$rewriteCreatorBeforeDone && array_key_exists($rewriteCreatorFieldBefore, $this->$type))
					{
						$nameString = $this->{$type}[$rewriteCreatorField] . $nameString;
						$rewriteCreatorBeforeDone = TRUE;
					}
					else if(!$rewriteCreatorBeforeDone && 
						!array_key_exists($rewriteCreatorFieldEach, $this->$type))
						$rewriteCreatorFinal = $this->{$type}[$rewriteCreatorField];
				}
			}
//print "$nameString<P>";
			$cArray[] = $nameString;
			$first = FALSE;
		}
/**
* Keep only some elements in array if we've exceeded $moreThan
*/
		$etAl = FALSE;
		if($style[$limit] && (sizeof($cArray) > $style[$moreThan]))
		{
			array_splice($cArray, $style[$limit]);
			if(isset($style[$italics]))
				$etAl = "[i]" . $style[$abbreviation] . "[/i]";
			else
				$etAl = $style[$abbreviation];
		}
/**
* add delimiters
*/
		if(sizeof($cArray) > 1)
		{
			if(sizeof($cArray) == 2)
				$cArray[0] .= $style[$delimitTwo];
			else
			{
				for($index = 0; $index < (sizeof($cArray) - 2); $index++)
				{
					if(!$index)
						$cArray[$index] .= $style[$delimitFirstBetween];
					else
						$cArray[$index] .= $style[$delimitNextBetween];
				}
				$cArray[sizeof($cArray) - 2] .= $style[$delimitLast];
			}
		}
/**
* If sizeof of $cArray > 1 or $etAl != FALSE, set this $nameType_plural to TRUE
*/
		if((sizeof($cArray) > 1) || $etAl)
		{
//			$pluralKey = $nameType . "_plural";
			$this->$pluralKey = TRUE;
		}
/**
* Finally flatten array
*/
		if($etAl)
			$pString = implode('', $cArray) . $etAl;
		else
			$pString = implode('', $cArray);
		if($rewriteCreatorFinal)
			$pString .= $rewriteCreatorFinal;
/**
* Check for repeating primary creator list in subsequent bibliographic item.
*/
		if($nameType == 'creator1')
		{
			$tempString = $pString;
			if(($style['primaryCreatorRepeat'] == 2) && ($this->previousCreator == $pString))
				$pString = $style['primaryCreatorRepeatString'];
			else if(($style['primaryCreatorRepeat'] == 1) && ($this->previousCreator == $pString))
				$pString = ''; // don't print creator list
			$this->previousCreator = $tempString;
		}
		if($shortFootnote)
			return array($pString, $creatorIds);
// For use with generic footnote templates, we must also place 'creator1' string (if not called 'creator') into the 'creator' slot
		if(($nameType == 'creator1') && ($this->styleMap->{$type}[$nameType] != 'creator'))
			$this->item['creator'] = $pString;
		$this->item[$this->styleMap->{$type}[$nameType]] = $pString;
	}
/**
* Handle initials.
* @see formatNames()
* 
* @author	Mark Grimshaw
* @version	1
* 
* @param	$creator	Associative array of creator name e.g.
* <pre>
*	array(['surname'] => 'Grimshaw', ['firstname'] => Mark, ['initials'] => 'M N G', ['prefix'] => ))
* </pre>
* Initials must be space-delimited.
*
* @param	$initialsStyle
* @param	$firstNameInitial
* @return	Formatted string of initials.
*/
	function checkInitials(&$creator, $initialsStyle, $firstNameInitial)
	{
/**
* Format firstname
*/
		if($creator['firstname'] && !$firstNameInitial) // Full name
			$firstName = $creator['firstname'];
		else if($creator['firstname']) // Initial only of first name.  'firstname' field may actually have several 'firstnames'
		{
			$fn = split(" ", $creator['firstname']);
			$firstTime = TRUE;
			foreach($fn as $name)
			{
// May be the first name is a hyphenated name
// We separate each part of the name separated by a -
				$fn2 = split("-", trim($name));
				if($firstTime)
				{
					if(sizeof($fn2) == 1) // no hyphen
					{
						$firstNameInitialMake = 
							$this->utf8->utf8_strtoupper($this->utf8->utf8_substr($fn2[0], 0, 1));
						$firstTime = FALSE;
						continue;
					}
					$firstNameInitialMake = "";
					foreach($fn2 as $nameparts)
					{
						if($firstNameInitialMake && ($initialsStyle <= 1)) // needs '.'
							$firstNameInitialMake .= ".-";
						else if($firstNameInitialMake)
							$firstNameInitialMake .= "-";
						$firstNameInitialMake .= 
							$this->utf8->utf8_strtoupper($this->utf8->utf8_substr($nameparts, 0, 1));
						$firstTime = FALSE;
					}
				}
				else
				{
					if(sizeof($fn2) == 1) // no hyphen
					{
						$initials[] = $this->utf8->utf8_strtoupper($this->utf8->utf8_substr($fn2[0], 0, 1));
						continue;
					}
					$fn3 = '';
					foreach($fn2 as $nameparts)
					{
						if($fn3)
						{
							$fn3 .= "-";
							$initials[] = $fn3 . $this->utf8->utf8_strtoupper(substr(trim($nameparts), 0, 1));
						}
						else
							$fn3 = $this->utf8->utf8_strtoupper(substr(trim($nameparts), 0, 1));
					}
				}
			}
			if(isset($initials))
			{
				if($creator['initials'])
					$creator['initials'] = join(" " , $initials) . ' ' . $creator['initials'];
				else
					$creator['initials'] = join(" " , $initials);
			}
		}
/**
* Initials are stored as space-delimited characters.
* If no initials, return just the firstname or its initial in the correct format.
*/
		if(!$creator['initials'])
		{
			if(isset($firstName))	// full first name only
				return $firstName;
			if(isset($firstNameInitialMake) && $initialsStyle > 1) // First name initial with no '.'
				return $firstNameInitialMake;
			if(isset($firstNameInitialMake)) // First name initial with  '.'
			{
// replace hyphen with '.-' so we get 'J.-F' for example
				str_replace('-', '.-', $firstNameInitialMake);
				return $firstNameInitialMake . '.';
			}
			return ''; // nothing here
		}
		$initialsArray = explode(' ', $creator['initials']);
		if($initialsStyle <= 1) // needs '.' before any hyphen
		{
			foreach($initialsArray as $key => $i)
				$initialsArray[$key] = str_replace('-', '.-', $i);
		}
/**
* If firstname is initial only, prepend to array
*/
		if(isset($firstNameInitialMake))
			array_unshift($initialsArray, $firstNameInitialMake);
		if($initialsStyle == 0) // 'T. U. '
			$initials = implode('. ', $initialsArray) . '.';
		else if($initialsStyle == 1) // 'T.U.'
			$initials = implode('.', $initialsArray) . '.';
		else if($initialsStyle == 2) // 'T U '
			$initials = implode(' ', $initialsArray);
		else // 'TU '
			$initials = implode('', $initialsArray);
/**
* If we have a full first name, prepend it to $initials.
*/
		if(isset($firstName))
			return ($firstName . ' ' . $initials);
		return $initials;
	}
/**
* Add an item to $this->item array
*
* @author	Mark Grimshaw
* @version	1
*
* @param	$item		The item to be added.
* @param	$fieldName	The database fieldName of the item to be added
*/
	function addItem($item, $fieldName)
	{
		$type = $this->type;
		if($item === FALSE)
			return;
/**
* This item may already exist (e.g. edition field for WIKINDX)
*/
		if(isset($this->item) && array_key_exists($this->styleMap->{$type}[$fieldName], $this->item))
			return FALSE;
		$this->item[$this->styleMap->{$type}[$fieldName]] = $item;
	}
/**
* Add all remaining items to $this->item array
*
* @author	Mark Grimshaw
* @version	1
*
* @param	$row		The items to be added.
*/
	function addAllOtherItems($row)
	{
		$type = $this->type;
		foreach($row as $field => $value)
		{
			if(array_key_exists($field, $this->styleMap->$type) && 
				!array_key_exists($this->styleMap->{$type}[$field], $this->item))
			{
				$item = $row[$field];
				$this->addItem($item, $field);
			}
		}
	}
/**
* Format a title.  Anything enclosed in $delimitLeft...$delimitRight is to be left unchanged
*
* @author	Mark Grimshaw
* @version	1
*
* @param	$pString	Raw title string.
* @param	$delimitLeft
* @param	$delimitRight
* @return	Formatted title string.
*/
	function formatTitle($pString, $delimitLeft = FALSE, $delimitRight = FALSE)
	{
		$type = $this->type;
		if(!array_key_exists('title', $this->styleMap->$type))
		{
			$this->item[$this->styleMap->{$type}['title']] = '';
			return;
		}
		if(array_key_exists($this->styleMap->{$type}['title'], $this->item)) // i.e. we're now dealing with subtitle
			$this->item[$this->styleMap->{$type}['title']] .= 
				$this->titleCapitalization($pString, $delimitLeft, $delimitRight);
		else
			$this->item[$this->styleMap->{$type}['title']] = 
				$this->titleCapitalization($pString, $delimitLeft, $delimitRight);
	}
/**
* Format a shortTitle.  Anything enclosed in $delimitLeft...$delimitRight is to be left unchanged
*
* @author	Mark Grimshaw
* @version	1
*
* @param	$pString	Raw title string.
* @param	$delimitLeft
* @param	$delimitRight
* @return	Formatted title string.
*/
	function formatShortTitle($pString, $delimitLeft = FALSE, $delimitRight = FALSE)
	{
		$type = $this->type;
		if(!array_key_exists('shortTitle', $this->styleMap->$type))
		{
			$this->item[$this->styleMap->{$type}['shortTitle']] = '';
			return;
		}
		$this->item[$this->styleMap->{$type}['shortTitle']] = 
			$this->titleCapitalization($pString, $delimitLeft, $delimitRight);
	}
/**
* Format a title of the original work from which a translation has been made.  Anything enclosed in $delimitLeft...$delimitRight is to be left unchanged
*
* @author	Mark Grimshaw
* @version	1
*
* @param	$pString	Raw title string.
* @param	$delimitLeft
* @param	$delimitRight
* @return	Formatted title string.
*/
	function formatTransTitle($pString, $delimitLeft = FALSE, $delimitRight = FALSE)
	{
		$type = $this->type;
		if(!array_key_exists('transTitle', $this->styleMap->$type))
			return;
		if(array_key_exists($this->styleMap->{$type}['transTitle'], $this->item)) // i.e. we're now dealing with subtitle
			$this->item[$this->styleMap->{$type}['transTitle']] .= 
				$this->titleCapitalization($pString, $delimitLeft, $delimitRight);
		else
			$this->item[$this->styleMap->{$type}['transTitle']] = 
				$this->titleCapitalization($pString, $delimitLeft, $delimitRight);
	}
/*
* Do title capitalization
*/
	function titleCapitalization($pString, $delimitLeft, $delimitRight)
	{
/**
* '0' == 'Osbib Bibliographic Formatting'
* '1' == 'Osbib bibliographic formatting'
*/
		$oldString = ltrim($pString);
		$oldDelimitLeft = $delimitLeft;
		if($this->style['titleCapitalization'])
		{
			if(!$delimitLeft)
				$delimitLeft = '{';
			if(!$delimitRight)
				$delimitRight = '}';
			$delimitLeft = preg_quote($delimitLeft);
			$delimitRight = preg_quote($delimitRight);
			$match = "/" . $delimitLeft . "/";
// Something here (preg_split probably) interferes with UTF-8 encoding (data is stored in 
// the database as UTF-8 as long as web browser charset == UTF-8).  
// So first decode then encode back to UTF-8 at end.
// There is a 'u' UTF-8 parameter for preg_xxx but it doesn't work.
			$pString = $this->utf8->decodeUtf8($pString);
			$newString = '';
			while(preg_match($match, $pString))
			{
				$array = preg_split("/(.*)$delimitLeft(.*)$delimitRight(.*)/U", 
					$pString, 2, PREG_SPLIT_DELIM_CAPTURE);
/**
* in case user has input {..} incorrectly
*/
				if(sizeof($array) == 1)
					break;
				$newString .= $this->utf8->utf8_strtolower($this->utf8->encodeUtf8($array[1])) . $this->utf8->encodeUtf8($array[2]);
				$pString = $array[4];
			}
			$newString .= $this->utf8->utf8_strtolower($this->utf8->encodeUtf8($pString));
		}
		$pString = isset($newString) ? $newString : $pString;
		if(strlen($oldString)>0) // perhaps requires first character maintained as is
			if($oldString{0} == $oldDelimitLeft) 
				return ltrim($pString);
		return $this->utf8->utf8_ucfirst(ltrim($pString));
	}
/**
* Format pages.
* $this->style['pageFormat']:
* 0 == 132-9
* 1 == 132-39
* 2 == 132-139
* 
* @author	Mark Grimshaw
* @version	1
*
* @param	$start		Page start.
* @param	$end		Page end.
* @param	$citation	If called from CITEFORMAT, this is the array of citation stylings.
* @return	string of pages.
*/
	function formatPages($start, $end = FALSE, $citation = FALSE)
	{
		$type = $this->type;
		$style = $citation ? $citation : $this->style;
/**
* Set default plural behaviour for pages
*/
		$this->pages_plural = FALSE;
/**
* If no page end, return just $start;
*/
		if(!$end)
		{
			$this->item[$this->styleMap->{$type}['pages']] = $start;
			return;
		}
/**
* Pages may be in roman numeral format etc.  Return unchanged
*/
		if(!is_numeric($start))
		{
			$this->item[$this->styleMap->{$type}['pages']] = $start . 'WIKINDX_NDASH' . $end;
			return;
		}
/**
* We have multiple pages...
*/
		$this->pages_plural = TRUE;
/**
* They've done something wrong so give them back exactly what they entered
*/
		if(($end <= $start) || (strlen($end) < strlen($start)))
		{
			$this->item[$this->styleMap->{$type}['pages']] = $start . 'WIKINDX_NDASH' . $end;
			return;
		}
		else if($style['pageFormat'] == 2)
		{
			$this->item[$this->styleMap->{$type}['pages']] = $start . 'WIKINDX_NDASH' . $end;
			return;
		}
		else
		{
/**
* We assume page numbers are not into the 10,000 range - if so, return the complete pages
*/
			if(strlen($start) <= 4)
			{
				$startArray = preg_split('//', $start);
				array_shift($startArray); // always an empty element at start?
				array_pop($startArray); // always an empty array element at end?
				if($style['pageFormat'] == 0)
				{
					array_pop($startArray);
					$endPage = substr($end, -1);
					$index = -2;
				}
				else
				{
					array_pop($startArray);
					array_pop($startArray);
					$endPage = substr($end, -2);
					$index = -3;
				}
				while(!empty($startArray))
				{
					$startPop = array_pop($startArray);
					$endSub = substr($end, $index--, 1);
					if($endSub == $startPop)
					{
						$this->item[$this->styleMap->{$type}['pages']] 
							= $start . '-' . $endPage;
						return;
					}
					if($endSub > $startPop)
						$endPage = $endSub . $endPage;
				}
			}
			else
			{
				$this->item[$this->styleMap->{$type}['pages']] = $start . 'WIKINDX_NDASH' . $end;
				return;
			}
		}
/**
* We should never reach here - in case we do, give back complete range so that something at least is printed
*/
		$this->item[$this->styleMap->{$type}['pages']] = $start . 'WIKINDX_NDASH' . $end;
	}
/**
* Format runningTime for film/broadcast
*
* @author	Mark Grimshaw
* @version	1
*
* @param	$minutes
* @param	$hours
*/
	function formatRunningTime($minutes, $hours)
	{
		$type = $this->type;
		if($this->style['runningTimeFormat'] == 0) // 3'45"
		{
			if(isset($minutes) && $minutes)
			{
				if($minutes < 10)
					$minutes = '0' . $minutes;
				$runningTime = $hours . "'" . $minutes . "\"";
			}
			else
				$runningTime = $hours . "'00\"";
		}
		else if($this->style['runningTimeFormat'] == 1) // 3:45
		{
			if(isset($minutes) && $minutes)
			{
				if($minutes < 10)
					$minutes = '0' . $minutes;
				$runningTime = $hours . ":" . $minutes;
			}
			else
				$runningTime = $hours . ":00";
		}
		else if($this->style['runningTimeFormat'] == 1) // 3,45
		{
			if(isset($minutes) && $minutes)
			{
				if($minutes < 10)
					$minutes = '0' . $minutes;
				$runningTime = $hours . "," . $minutes;
			}
			else
				$runningTime = $hours . ",00";
		}
		else if($this->style['runningTimeFormat'] == 3) // 3 hours, 45 minutes
		{
			$hours = ($hours == 1) ? $hours . " hour" : $hours . " hours";
			if(isset($minutes) && $minutes)
			{
				$minutes = ($minutes == 1) ? $minutes . " minute" : $minutes . " minutes";
				$runningTime = $hours . ", " . $minutes;
			}
			else
				$runningTime = $hours;
		}
		else if($this->style['runningTimeFormat'] == 4) // 3 hours and 45 minutes
		{
			$hours = ($hours == 1) ? $hours . " hour" : $hours . " hours";
			if(isset($minutes) && $minutes)
			{
				$minutes = ($minutes == 1) ? $minutes . " minute" : $minutes . " minutes";
				$runningTime = $hours . " and " . $minutes;
			}
			else
				$runningTime = $hours;
		}
		$this->item[$this->styleMap->{$type}['runningTime']] = $runningTime;
	}
/**
* Format date
*
* @author	Mark Grimshaw
* @version	2
*
* @param	INT $startDay
* @param	INT $startMonth
* @param	INT $endDay
* @param	INT $sendMonth
*/
	function formatDate($startDay, $startMonth, $endDay, $endMonth)
	{
		$type = $this->type;
		$oldStartDay = $startDay;
		$oldEndDay = $endDay;
		if($this->dateMonthNoDay && !$startDay && !$endDay)
			$this->{$type}[$this->styleMap->{$type}['date']] = 
			$this->dateArray[$this->styleMap->{$type}['date']];
		if($startDay !== FALSE)
		{
			if($this->style['dayFormat'] == 1) // e.g. 10.
				$startDay .= '.';
			else if($this->style['dayFormat'] == 2) // e.g. 10th
				$startDay = $this->cardinalToOrdinal($startDay, 'dayMonth');
			if(array_key_exists('dayLeadingZero', $this->style) && $oldStartDay < 10)
				$startDay = '0' . $startDay;
		}
		if($endDay !== FALSE)
		{
			if($this->style['dayFormat'] == 1) // e.g. 10.
				$endDay .= '.';
			else if($this->style['dayFormat'] == 2) // e.g. 10th
				$endDay = $this->cardinalToOrdinal($endDay, 'dayMonth');
			if(array_key_exists('dayLeadingZero', $this->style) && $oldEndDay < 10)
				$endDay = '0' . $endDay;
		}
		if($this->style['monthFormat'] == 1) // Full month name
			$monthArray = $this->longMonth;
		else if($this->style['monthFormat'] == 2) // User-defined
		{
			for($i = 1; $i <= 12; $i++)
				$monthArray[$i] = $this->style["userMonth_$i"];
		}
		else // Short month name
			$monthArray = $this->shortMonth;
		if($startMonth !== FALSE)
			$startMonth = $monthArray[$startMonth];
		if($endMonth !== FALSE)
			$endMonth = $monthArray[$endMonth];
		if(!$endMonth)
		{
			if($this->style['dateFormat']) // Order == Month Day
			{
				$startDay = ($startDay === FALSE) ? '' : ' ' . $startDay;
				$date = $startMonth . $startDay;
			}
			else // Order == Day Month
			{
				$startDay = ($startDay === FALSE) ? '' : $startDay . ' ';
				$date = $startDay . $startMonth;
			}
		}
		else // date range
		{
			if(!$startDay)
				$delimit = $this->style['dateRangeDelimit2'];
			else
				$delimit = $this->style['dateRangeDelimit1'];
			if(($endMonth !== FALSE) && ($startMonth == $endMonth) && ($this->style['dateRangeSameMonth'] == 1))
			{
				$endMonth = FALSE;
				if(!$endDay)
					$delimit = FALSE;
			}
			if($this->style['dateFormat']) // Order == Month Day
			{
				$startDay = ($startDay === FALSE) ? '' : ' ' . $startDay;
				$startDate = $startMonth . $startDay;
				if($endMonth)
					$endDate = $endMonth . $endDay = ($endDay === FALSE) ? '' : ' ' . $endDay;
				else
					$endDate = $endDay;
			}
			else // Order == Day Month
			{
				if($endMonth)
				{
					$startDate = $startDay . ' ' . $startMonth;
					$endDate = $endDay = ($endDay === FALSE) ? '' : $endDay . ' ';
					$endDate .= $endMonth;
				}
				else
				{
					$startDate = $startDay;
					$endDate = ($endDay === FALSE) ? ' ' : $endDay . ' ';
					$endDate .= $startMonth;
				}
			}
			$date = $startDate . $delimit . $endDate;
		}
		$this->item[$this->styleMap->{$type}['date']] = $date;
	}
/**
* Format edition
*
* @author	Mark Grimshaw
* @version	1
*
* @param	INT $edition
* @return	string of edition.
*/
	function formatEdition($edition)
	{
		$type = $this->type;
		if(!is_numeric($edition))
			$edition = $edition;
		else if($this->style['editionFormat'] == 1) // 10.
			$edition .= '.';
		else if($this->style['editionFormat'] == 2) // 10th
			$edition = $this->cardinalToOrdinal($edition, 'edition');
		$this->item[$this->styleMap->{$type}[array_search('edition', $this->styleMap->$type)]] = $edition;
	}
/**
* Create ordinal number from cardinal
*
* @author	Mark Grimshaw
* @version	1
*
* @param	$cardinal
* @return	$ordinal
*/
	function cardinalToOrdinal($cardinal, $field = FALSE)
	{
// WIKINDX-specific
		if($this->wikindx && method_exists($this->wikindxLanguageClass, "cardinalToOrdinal"))
			return $this->wikindxLanguageClass->cardinalToOrdinal($cardinal, $field);
		$modulo = $cardinal % 100;
		if(($modulo == 11) || ($modulo == 12) || ($modulo == 13))
			return $cardinal . 'th';
		$modulo = $cardinal % 10;
		if(($modulo >= 4) || !$modulo)
			return $cardinal . 'th';
		if($modulo == 1)
			return $cardinal . 'st';
		if($modulo == 2)
			return $cardinal . 'nd';
		if($modulo == 3)
			return $cardinal . 'rd';
	}
/**
* Localisations etc.
* @author	Mark Grimshaw
* @version	1
*/
	function loadArrays()
	{
// WIKINDX-specific
		if($this->wikindx)
		{
			$languageDir = $this->style['localisation'];
			if(!is_dir("languages/$languageDir"))
				$languageDir = "en";
			include_once("languages/$languageDir/CONSTANTS.php");
			$class = "CONSTANTS_" . $languageDir;
			$this->wikindxLanguageClass = new $class();
			if(method_exists($this->wikindxLanguageClass, "monthToLongName")
				&& method_exists($this->wikindxLanguageClass, "monthToShortName"))
			{
				$this->longMonth = $this->wikindxLanguageClass->monthToLongName();
				$this->shortMonth = $this->wikindxLanguageClass->monthToShortName();
				return;
			}
		}
// Defaults
		$this->longMonth = array(
				1	=>	'January',
				2	=>	'February',
				3	=>	'March',
				4	=>	'April',
				5	=>	'May',
				6	=>	'June',
				7	=>	'July',
				8	=>	'August',
				9	=>	'September',
				10	=>	'October',
				11	=>	'November',
				12	=>	'December',
			);
		$this->shortMonth = array(
				1	=>	'Jan',
				2	=>	'Feb',
				3	=>	'Mar',
				4	=>	'Apr',
				5	=>	'May',
				6	=>	'Jun',
				7	=>	'Jul',
				8	=>	'Aug',
				9	=>	'Sep',
				10	=>	'Oct',
				11	=>	'Nov',
				12	=>	'Dec',
			);
		$this->titleSubtitleSeparator = ": ";
	}
/*
 * convertEntry - convert any laTeX code and convert to UTF-8 ready for storing in the database (betex only)
 *
 * @author Mark Grimshaw, modified by Christophe Ambroise 26/10/2003
 * @param array $entry - a bibtex entry
 * @return array $entry converted to utf8
 */
	function convertEntry($entry)
	{
		$this->config = new BIBTEXCONFIG();
		$this->config->bibtex();
// Construction of the transformation filter
		foreach($this->config->bibtexSpCh as $key => $value)
		{
			$replaceBibtex[] = chr($key);
			$matchBibtex[] = preg_quote("/$value/");
		}
		foreach($this->config->bibtexSpChOld as $key => $value)
		{
			$replaceBibtex[] = chr($key);
			$matchBibtex[] = preg_quote("/$value/");
		}
		foreach($this->config->bibtexSpChOld2 as $key => $value)
		{
			$replaceBibtex[] = chr($key);
			$matchBibtex[] = preg_quote("/$value/");
		}
		foreach($this->config->bibtexSpChLatex as $key => $value)
		{
			$replaceBibtex[] =  chr($key);
			$matchBibtex[] = preg_quote("/$value/");
		}
// Processing of the entry
		foreach($entry as $key => $value)
		{
// The transformation filter  has returned  latin1 code
// We thus need to work with latin1.
// Title field may use {...} to indicate capitalization so temporarily replace with (hopefully) unique strings
			if($key == 'title')
			{
				$value = str_replace('{', 'OSBIBCAPITALLEFT', $value);
				$value = str_replace('}', 'OSBIBCAPITALRIGHT', $value);
			}
			$value= $this->utf8->smartUtf8_decode($value);
			$value = utf8_encode(preg_replace($matchBibtex, $replaceBibtex, $value));
			if($key == 'title')
			{
				$value = str_replace('OSBIBCAPITALLEFT', '{', $value);
				$value = str_replace('OSBIBCAPITALRIGHT', '}', $value);
			}
			$entry[$key] = $value;
		}
		return $entry;
	}
}
/*****
 * BIBTEXCONFIG: BibTeX Configuration class
 *****/

class BIBTEXCONFIG
{
// Constructor
  function BIBTEXCONFIG()
  {
  }

// BibTeX arrays
  function bibtex()
  {
    $this->bibtexSpCh = array(
// Deal with '{' and '}' first!
			      0x007B	=>	"\\textbraceleft",
			      0x007D	=>	"\\textbraceright",
			      0x0022	=>	"{\"}",
			      0x0023	=>	"{\#}",
			      0x0025	=>	"{\%}",
			      0x0026	=>	"{\&}",
			      0x003C	=>	"\\textless",
			      0x003E	=>	"\\textgreater",
			      0x005F	=>	"{\_}",
			      0x00A3	=>	"\\textsterling",
			      0x00C0	=>	"{\`A}",
			      0x00C1	=>	"{\'A}",
			      0x00C2	=>	"{\^A}",
			      0x00C3	=>	"{\~A}",
			      0x00C4	=>	'{\"A}',
			      0x00C5	=>	"{\AA}",
			      0x00C6	=>	"{\AE}",
			      0x00C7	=>	"{\c{C}}",
			      0x00C8	=>	"{\`E}",
			      0x00C9	=>	"{\'E}",
			      0x00CA	=>	"{\^E}",
			      0x00CB	=>	'{\"E}',
			      0x00CC	=>	"{\`I}",
			      0x00CD	=>	"{\'I}",
			      0x00CE	=>	"{\^I}",
			      0x00CF	=>	'{\"I}',
			      0x00D1	=>	"{\~N}",
			      0x00D2	=>	"{\`O}",
			      0x00D3	=>	"{\'O}",
			      0x00D4	=>	"{\^O}",
			      0x00D5	=>	"{\~O}",
			      0x00D6	=>	'{\"O}',
			      0x00D8	=>	"{\O}",
			      0x00D9	=>	"{\`U}",
			      0x00DA	=>	"{\'U}",
			      0x00DB	=>	"{\^U}",
			      0x00DC	=>	'{\"U}',
			      0x00DD	=>	"{\'Y}",
			      0x00DF	=>	"{\ss}",
			      0x00E0	=>	"{\`a}",
			      0x00E1	=>	"{\'a}",
			      0x00E2	=>	"{\^a}",
			      0x00E3	=>	"{\~a}",
			      0x00E4	=>	'{\"a}',
			      0x00E5	=>	"{\aa}",
			      0x00E6	=>	"{\ae}",
			      0x00E7	=>	"{\c{c}}",
			      0x00E8	=>	"{\`e}",
			      0x00E9	=>	"{\'e}",
			      0x00EA	=>	"{\^e}",
			      0x00EB	=>	'{\"e}',
			      0x00EC	=>	"{\`\i}",
			      0x00ED	=>	"{\'\i}",
			      0x00EE	=>	"{\^\i}",
			      0x00EF	=>	'{\"\i}',
			      0x00F1	=>	"{\~n}",
			      0x00F2	=>	"{\`o}",
			      0x00F3	=>	"{\'o}",
			      0x00F4	=>	"{\^o}",
			      0x00F5	=>	"{\~o}",
			      0x00F6	=>	'{\"o}',
			      0x00F8	=>	"{\o}",
			      0x00F9	=>	"{\`u}",
			      0x00FA	=>	"{\'u}",
			      0x00FB	=>	"{\^u}",
			      0x00FC	=>	'{\"u}',
			      0x00FD	=>	"{\'y}",
			      0x00FF	=>	'{\"y}',
			      0x00A1	=>	"{\!}",
			      0x00BF	=>	"{\?}",
			      );
//Old style with extra {} - usually array_flipped
    $this->bibtexSpChOld = array(
				 0x00C0	=>	"{\`{A}}",
				 0x00C1	=>	"{\'{A}}",
				 0x00C2	=>	"{\^{A}}",
				 0x00C3	=>	"{\~{A}}",
				 0x00C4	=>	'{\"{A}}',
				 0x00C5	=>	"{\A{A}}",
				 0x00C6	=>	"{\A{E}}",
				 0x00C7	=>	"{\c{C}}",
				 0x00C8	=>	"{\`{E}}",
				 0x00C9	=>	"{\'{E}}",
				 0x00CA	=>	"{\^{E}}",
				 0x00CB	=>	'{\"{E}}',
				 0x00CC	=>	"{\`{I}}",
				 0x00CD	=>	"{\'{I}}",
				 0x00CE	=>	"{\^{I}}",
				 0x00CF	=>	'{\"{I}}',
				 0x00D1	=>	"{\~{N}}",
				 0x00D2	=>	"{\`{O}}",
				 0x00D3	=>	"{\'{O}}",
				 0x00D4	=>	"{\^{O}}",
				 0x00D5	=>	"{\~{O}}",
				 0x00D6	=>	'{\"{O}}',
				 0x00D8	=>	"{\{O}}",
				 0x00D9	=>	"{\`{U}}",
				 0x00DA	=>	"{\'{U}}",
				 0x00DB	=>	"{\^{U}}",
				 0x00DC	=>	'{\"{U}}',
				 0x00DD	=>	"{\'{Y}}",
				 0x00DF	=>	"{\s{s}}",
				 0x00E0	=>	"{\`{a}}",
				 0x00E1	=>	"{\'{a}}",
				 0x00E2	=>	"{\^{a}}",
				 0x00E3	=>	"{\~{a}}",
				 0x00E4	=>	'{\"{a}}',
				 0x00E5	=>	"{\a{a}}",
				 0x00E6	=>	"{\a{e}}",
				 0x00E7	=>	"{\c{c}}",
				 0x00E8	=>	"{\`{e}}",
				 0x00E9	=>	"{\'{e}}",
				 0x00EA	=>	"{\^{e}}",
				 0x00EB	=>	'{\"{e}}',
				 0x00EC	=>	"{\`\i}",
				 0x00ED	=>	"{\'\i}",
				 0x00EE	=>	"{\^\i}",
				 0x00EF	=>	'{\"\i}',
				 0x00F1	=>	"{\~{n}}",
				 0x00F2	=>	"{\`{o}}",
				 0x00F3	=>	"{\'{o}}",
				 0x00F4	=>	"{\^{o}}",
				 0x00F5	=>	"{\~{o}}",
				 0x00F6	=>	'{\"{o}}',
				 0x00F8	=>	"{\{o}}",
				 0x00F9	=>	"{\`{u}}",
				 0x00FA	=>	"{\'{u}}",
				 0x00FB	=>	"{\^{u}}",
				 0x00FC	=>	'{\"{u}}',
				 0x00FD	=>	"{\'{y}}",
				 0x00FF	=>	'{\"{y}}',
				 0x00A1	=>	"{\{!}}",
				 0x00BF	=>	"{\{?}}",
				 );
// And there's more?!?!?!?!? (This is not strict bibtex.....)
    $this->bibtexSpChOld2 = array(
				  0x00C0	=>	"\`{A}",
				  0x00C1	=>	"\'{A}",
				  0x00C2	=>	"\^{A}",
				  0x00C3	=>	"\~{A}",
				  0x00C4	=>	'\"{A}',
				  0x00C5	=>	"\A{A}",
				  0x00C6	=>	"\A{E}",
				  0x00C7	=>	"\c{C}",
				  0x00C8	=>	"\`{E}",
				  0x00C9	=>	"\'{E}",
				  0x00CA	=>	"\^{E}",
				  0x00CB	=>	'\"{E}',
				  0x00CC	=>	"\`{I}",
				  0x00CD	=>	"\'{I}",
				  0x00CE	=>	"\^{I}",
				  0x00CF	=>	'\"{I}',
				  0x00D1	=>	"\~{N}",
				  0x00D2	=>	"\`{O}",
				  0x00D3	=>	"\'{O}",
				  0x00D4	=>	"\^{O}",
				  0x00D5	=>	"\~{O}",
				  0x00D6	=>	'\"{O}',
				  0x00D8	=>	"\{O}",
				  0x00D9	=>	"\`{U}",
				  0x00DA	=>	"\'{U}",
				  0x00DB	=>	"\^{U}",
				  0x00DC	=>	'\"{U}',
				  0x00DD	=>	"\'{Y}",
				  0x00DF	=>	"\s{s}",
				  0x00E0	=>	"\`{a}",
				  0x00E1	=>	"\'{a}",
				  0x00E2	=>	"\^{a}",
				  0x00E3	=>	"\~{a}",
				  0x00E4	=>	'\"{a}',
				  0x00E5	=>	"\a{a}",
				  0x00E6	=>	"\a{e}",
				  0x00E7	=>	"\c{c}",
				  0x00E8	=>	"\`{e}",
				  0x00E9	=>	"\'{e}",
				  0x00EA	=>	"\^{e}",
				  0x00EB	=>	'\"{e}',
				  0x00EC	=>	"\`{i}",
				  0x00ED	=>	"\'{i}",
				  0x00EE	=>	"\^{i}",
				  0x00EF	=>	'\"{i}',
				  0x00F1	=>	"\~{n}",
				  0x00F2	=>	"\`{o}",
				  0x00F3	=>	"\'{o}",
				  0x00F4	=>	"\^{o}",
				  0x00F5	=>	"\~{o}",
				  0x00F6	=>	'\"{o}',
				  0x00F8	=>	"\{o}",
				  0x00F9	=>	"\`{u}",
				  0x00FA	=>	"\'{u}",
				  0x00FB	=>	"\^{u}",
				  0x00FC	=>	'\"{u}',
				  0x00FD	=>	"\'{y}",
				  0x00FF	=>	'\"{y}',
				  0x00A1	=>	"\{!}",
				  0x00BF	=>	"\{?}",
				  );
// Latex code that some bibtex users may be using
    $this->bibtexSpChLatex = array(
				   0x00C0	=>	"\`A",
				   0x00C1	=>	"\'A",
				   0x00C2	=>	"\^A",
				   0x00C3	=>	"\~A",
				   0x00C4	=>	'\"A',
				   0x00C5	=>	"\AA",
				   0x00C6	=>	"\AE",
				   0x00C7	=>	"\cC",
				   0x00C8	=>	"\`E",
				   0x00C9	=>	"\'E",
				   0x00CA	=>	"\^E",
				   0x00CB	=>	'\"E',
				   0x00CC	=>	"\`I",
				   0x00CD	=>	"\'I",
				   0x00CE	=>	"\^I",
				   0x00CF	=>	'\"I',
				   0x00D1	=>	"\~N",
				   0x00D2	=>	"\`O",
				   0x00D3	=>	"\'O",
				   0x00D4	=>	"\^O",
				   0x00D5	=>	"\~O",
				   0x00D6	=>	'\"O',
				   0x00D8	=>	"\O",
				   0x00D9	=>	"\`U",
				   0x00DA	=>	"\'U",
				   0x00DB	=>	"\^U",
				   0x00DC	=>	'\"U',
				   0x00DD	=>	"\'Y",
				   0x00DF	=>	"\ss",
				   0x00E0	=>	"\`a",
				   0x00E1	=>	"\'a",
				   0x00E2	=>	"\^a",
				   0x00E3	=>	"\~a",
				   0x00E4	=>	'\"a',
				   0x00E5	=>	"\aa",
				   0x00E6	=>	"\ae",
				   0x00E7	=>	"\cc",
				   0x00E8	=>	"\`e",
				   0x00E9	=>	"\'e",
				   0x00EA	=>	"\^e",
				   0x00EB	=>	'\"e',
				   0x00EC	=>	"\`i",
				   0x00ED	=>	"\'i",
				   0x00EE	=>	"\^i",
				   0x00EF	=>	'\"i',
				   0x00F1	=>	"\~n",
				   0x00F2	=>	"\`o",
				   0x00F3	=>	"\'o",
				   0x00F4	=>	"\^o",
				   0x00F5	=>	"\~o",
				   0x00F6	=>	'\"o',
				   0x00F8	=>	"\o",
				   0x00F9	=>	"\`u",
				   0x00FA	=>	"\'u",
				   0x00FB	=>	"\^u",
				   0x00FC	=>	'\"u',
				   0x00FD	=>	"\'y",
				   0x00FF	=>	'\"y',
				   0x00A1	=>	"\!",
				   0x00BF	=>	"\?",
				   );
    $this->bibtexSpChPlain = array(
				   0x00C0	=>	"A",
				   0x00C1	=>	"A",
				   0x00C2	=>	"A",
				   0x00C3	=>	"A",
				   0x00C4	=>	'A',
				   0x00C5	=>	"A",
				   0x00C6	=>	"AE",
				   0x00C7	=>	"C",
				   0x00C8	=>	"E",
				   0x00C9	=>	"E",
				   0x00CA	=>	"E",
				   0x00CB	=>	'E',
				   0x00CC	=>	"I",
				   0x00CD	=>	"I",
				   0x00CE	=>	"I",
				   0x00CF	=>	'I',
				   0x00D1	=>	"N",
				   0x00D2	=>	"O",
				   0x00D3	=>	"O",
				   0x00D4	=>	"O",
				   0x00D5	=>	"O",
				   0x00D6	=>	'O',
				   0x00D8	=>	"O",
				   0x00D9	=>	"U",
				   0x00DA	=>	"U",
				   0x00DB	=>	"U",
				   0x00DC	=>	'U',
				   0x00DD	=>	"Y",
				   0x00DF	=>	"ss",
				   0x00E0	=>	"a",
				   0x00E1	=>	"a",
				   0x00E2	=>	"a",
				   0x00E3	=>	"a",
				   0x00E4	=>	'a',
				   0x00E5	=>	"aa",
				   0x00E6	=>	"ae",
				   0x00E7	=>	"c",
				   0x00E8	=>	"e",
				   0x00E9	=>	"e",
				   0x00EA	=>	"e",
				   0x00EB	=>	'e',
				   0x00EC	=>	"i",
				   0x00ED	=>	"i",
				   0x00EE	=>	"i",
				   0x00EF	=>	'i',
				   0x00F1	=>	"n",
				   0x00F2	=>	"o",
				   0x00F3	=>	"o",
				   0x00F4	=>	"o",
				   0x00F5	=>	"o",
				   0x00F6	=>	'o',
				   0x00F8	=>	"o",
				   0x00F9	=>	"u",
				   0x00FA	=>	"u",
				   0x00FB	=>	"u",
				   0x00FC	=>	'u',
				   0x00FD	=>	"u",
				   0x00FF	=>	'u',
				   0x00A1	=>	"u",
				   0x00BF	=>	"u",
				   );
  }
}
?>