1<?php
2  /********************************
3   OSBib:
4   A collection of PHP classes to create and manage bibliographic formatting for OS bibliography software
5   using the OSBib standard.
6
7   Released through http://bibliophile.sourceforge.net under the GPL licence.
8   Do whatever you like with this -- some credit to the author(s) would be appreciated.
9
10   If you make improvements, please consider contacting the administrators at bibliophile.sourceforge.net
11   so that your improvements can be added to the release package.
12
13   Mark Grimshaw 2005
14   http://bibliophile.sourceforge.net
15  ********************************/
16
17  /** Description of class BIBFORMAT
18   * Format a bibliographic resource for output.
19   *
20   * @author	Mark Grimshaw
21   * @version	1
22   */
23class BIBFORMAT
24{
25  /**
26   * $dir is the path to STYLEMAP.php etc.
27   */
28  function BIBFORMAT($dir = FALSE, $bibtex = FALSE, $preview = FALSE)
29  {
30    //05/05/2005 G.GARDEY: add a last "/" to $stylePath if not present.
31    $this->preview = $preview;
32    if(!$this->preview) // Not javascript preview
33      {
34	$dir = trim($dir);
35	if(!$dir){
36	  $this->dir = dirname(__FILE__) . "/";
37	}
38	else{
39	  $this->dir = $dir;
40	  if($dir[strlen($dir)-1] != "/"){
41	    $this->dir .= "/";
42	  }
43	}
44	$this->bibtexParsePath  = $this->dir . "format/bibtexParse";
45      }
46    else // preview
47      $this->dir = '';
48    $this->bibtex = $bibtex;
49    if($this->bibtex)
50      {
51	include_once($this->dir."STYLEMAPBIBTEX.php");
52	$this->styleMap = new STYLEMAPBIBTEX();
53      }
54    else
55      {
56	include_once($this->dir."STYLEMAP.php");
57	$this->styleMap = new STYLEMAP();
58      }
59    include_once($this->dir."UTF8.php");
60    $this->utf8 = new UTF8();
61    /**
62     * Highlight preg pattern and CSS class for HTML display
63     */
64    $this->patterns = FALSE;
65    $this->patternHighlight = FALSE;
66    /**
67     * Output medium:
68     * Defaul 'html'
69     */
70    $this->output = 'html';
71    $this->previousCreator = '';
72    /**
73     * Switch editor and author positions in the style definition for a book in which there are only editors
74     */
75    $this->editorSwitch = FALSE;
76    /**
77     * Load month arrays
78     */
79    $this->loadArrays();
80    /**
81     *  Convert the entry to produce utf8
82     *  Defaut: 'FALSE', we assume that the entries are already clean
83     */
84    $this->convertEntry=FALSE;
85  }
86  /**
87   * Read the chosen bibliographic style and create arrays based on resource type.
88   *
89   * @author	Mark Grimshaw
90   * @version	1
91   *
92   * @param	$stylePath	The path where the styles are.
93   * @param	$style		The requested bibliographic output style.
94   * @return	BOOLEAN
95   */
96  function loadStyle($stylePath, $style)
97  {
98    //05/05/2005 G.GARDEY: add a last "/" to $stylePath if not present.
99    $stylePath = trim($stylePath);
100    if($stylePath[strlen($stylePath)-1] != "/"){
101      $stylePath .= "/";
102    }
103    $uc = $stylePath . strtolower($style) . "/" . strtolower($style) . ".xml";
104    $lc = $stylePath . strtolower($style) . "/" . strtoupper($style) . ".xml";
105    $styleFile = file_exists($uc) ? $uc : $lc;
106    if(!$fh = fopen($styleFile, "r"))
107      return array(FALSE, FALSE, FALSE);
108    include_once($this->dir."PARSEXML.php");
109    $parseXML = new PARSEXML($this);
110    list($info, $citation, $common, $types) = $parseXML->extractEntries($fh);
111    fclose($fh);
112    return array($info, $citation, $common, $types);
113  }
114  /**
115   * Transform the raw data from the XML file into usable arrays
116   *
117   * @author	Mark Grimshaw
118   * @version	1
119   *
120   * @param	$common		Array of global formatting data
121   * @param	$types		Array of style definitions for each resource type
122   */
123  function getStyle($common, $types)
124  {
125    $this->commonToArray($common);
126    $this->typesToArray($types);
127  }
128  /**
129   * Reformat the array representation of common styling into a more useable format.
130   * 'common' styling refers to formatting that is common to all resource types such as creator formatting, title
131   * capitalization etc.
132   *
133   * @author	Mark Grimshaw
134   * @version	1
135   *
136   * @param	$common		nodal array representation of XML data
137   * @return	flattened array representation for easier use.
138   */
139  function commonToArray($common)
140  {
141    foreach($common as $array)
142      {
143	if(array_key_exists('_NAME', $array) && array_key_exists('_DATA', $array))
144	  $this->style[$array['_NAME']] = $array['_DATA'];
145      }
146  }
147  /**
148   * Reformat the array representation of resource types into arrays based on the type.
149   *
150   * @param	$types		nodal array representation of XML data
151   */
152  function typesToArray($types)
153  {
154    foreach($types as $resourceArray)
155      {
156	/**
157	 * The resource type which will be our array name
158	 */
159	$type = $resourceArray['_ATTRIBUTES']['name'];
160	$styleDefinition = $resourceArray['_ELEMENTS'];
161	foreach($styleDefinition as $array)
162	  {
163	    if(array_key_exists('_NAME', $array) && array_key_exists('_DATA', $array)
164	       && array_key_exists('_ELEMENTS', $array))
165	      {
166		if($array['_NAME'] == 'fallbackstyle')
167		  {
168		    $this->fallback[$type] = $array['_DATA'];
169		    break;
170		  }
171		if($array['_NAME'] == 'ultimate')
172		  {
173		    $this->{$type}['ultimate'] = $array['_DATA'];
174		    continue;
175		  }
176		foreach($array['_ELEMENTS'] as $elements)
177		  {
178		    if($array['_NAME'] == 'independent')
179		      {
180			$split = split("_", $elements['_NAME']);
181			$this->{$type}[$array['_NAME']][$split[1]]
182					= $elements['_DATA'];
183		      }
184		    else
185		      $this->{$type}[$array['_NAME']][$elements['_NAME']]
186				      = $elements['_DATA'];
187		  }
188	      }
189	  }
190	/**
191	 * Backup each $this->$type array.  If we need to switch editors, it's faster to restore each
192	 * $this->$type array from this backup than to reload the style file and parse it.
193	 */
194	if(isset($this->$type))
195	  $this->backup[$type] = $this->$type;
196      }
197  }
198  /**
199   * Restore each $this->type array from $this->backup
200   *
201   * @author	Mark Grimshaw
202   * @version	1
203   */
204  function restoreTypes()
205  {
206    foreach($this->backup as $type => $array)
207      $this->$type = $array;
208  }
209  /**
210   * Perform pre-processing on the raw SQL array
211   *
212   * @author	Mark Grimshaw
213   * @version	1
214   *
215   * @param	$type	The resource type
216   * @param	$row	Associate array of raw SQL data
217   * @return	$row	Processed row of raw SQL data
218   */
219  function preProcess($type, $row)
220  {
221    /**
222     * Ensure that $this->item is empty for each resource!!!!!!!!!!
223     */
224    $this->item = array();
225    // Map this system's resource type to OSBib's resource type
226    $this->type = array_search($type, $this->styleMap->types);
227    if($this->bibtex && array_key_exists('author', $row))
228      {
229	$row['creator1'] = $row['author'];
230	unset($row['author']);
231      }
232    if($this->bibtex && array_key_exists('editor', $row))
233      {
234	$row['creator2'] = $row['editor'];
235	unset($row['editor']);
236      }
237    /**
238     * Set any author/editor re-ordering for book and book_article type.
239     */
240    if(!$this->preview && (($type == 'book') || ($type == 'book_article')) &&
241       $row['creator2'] && !$row['creator1'] && $this->style['editorSwitch'] &&
242       array_key_exists('author', $this->$type))
243      {
244	$row['creator1'] = $row['creator2'];
245	$row['creator2'] = FALSE;
246	include_once($this->dir . "PARSESTYLE.php");
247	$editorArray = PARSESTYLE::parseStringToArray($type, $this->style['editorSwitchIfYes'],
248						      $this->styleMap);
249	if(!empty($editorArray) && array_key_exists('editor', $editorArray))
250	  {
251	    $this->{$type}['author'] = $editorArray['editor'];
252	    unset($this->{$type}['editor']);
253	    $this->editorSwitch = TRUE;
254	  }
255      }
256    /**
257     * If $row comes in in BibTeX format, process and add items to $this->item
258     */
259    if($this->bibtex)
260      {
261	if(!$this->type)
262	  {
263	    list($type, $row) = $this->preProcessBibtex($row, $type);
264	  } else
265	  list($type, $row) = $this->preProcessBibtex($row, $this->type);
266      }
267    /**
268     * Ensure that for theses types, the first letter of type and label are capitalized (e.g. 'Master's Thesis').
269     */
270    if($type == 'thesis')
271      {
272	if(($key = array_search('type', $this->styleMap->$type)) !== FALSE)
273	  {
274	    if(isset($row[$key]))
275	      $row[$key] = ucfirst($row[$key]);
276	  }
277	if(($key = array_search('label', $this->styleMap->$type)) !== FALSE)
278	  {
279	    if(isset($row[$key]))
280	      $row[$key] = ucfirst($row[$key]);
281	  }
282      }
283    /**
284     * Set to catch-all generic style.  For all keys except named database fields, creator1 and year1,
285     * we only print if the value in $this->styleMap matches the value in
286     * $this->styleMap->generic for each key.
287     */
288    if(!isset($this->$type))
289      {
290	$fallback = $this->fallback[$type];
291	$type = $fallback;
292      }
293    $this->type = $type;
294    /**
295     * Add BibTeX entry to $this->item
296     */
297    if($this->bibtex)
298      {
299	foreach($row as $field => $value)
300	  {
301	    if(array_key_exists($field, $this->styleMap->$type) &&
302	       !array_key_exists($this->styleMap->{$type}[$field], $this->item))
303	      $this->addItem($row[$field], $field);
304	  }
305      }
306    return $row;
307  }
308  /**
309   * Preprocess BibTeX-type entries
310   * @author Mark Grimshaw
311   * @version 1
312   *
313   * @param assoc. array of elements for one bibtex entry
314   * @param string resource type
315   * @return string resource type
316   * @return array resource assoc. array of elements for one bibtex entry
317   */
318  function preProcessBibtex(&$row, $type)
319  {
320    //05/05/2005 G.GARDEY: change bibtexParse name.
321    /**
322     * This set of includes is for the OSBib public release and should be uncommented for that and
323     * the WIKINDX-specific includes below commented out!
324     */
325    include_once($this->bibtexParsePath . "/PARSECREATORS.php");
326    $parseCreator = new PARSECREATORS();
327    include_once($this->bibtexParsePath . "/PARSEMONTH.php");
328    $parseDate = new PARSEMONTH();
329    include_once($this->bibtexParsePath . "/PARSEPAGE.php");
330    $parsePages = new PARSEPAGE();
331
332    // WIKINDX naming of above files
333    /*
334     include_once($this->bibtexParsePath . "/BIBTEXCREATORPARSE.php");
335     $parseCreator = new BIBTEXCREATORPARSE();
336     include_once($this->bibtexParsePath . "/BIBTEXMONTHPARSE.php");
337     $parseDate = new BIBTEXMONTHPARSE();
338     include_once($this->bibtexParsePath . "/BIBTEXPAGEPARSE.php");
339     $parsePages = new BIBTEXPAGEPARSE();
340
341
342
343    */
344    // Added by Christophe Ambroise: convert the bibtex entry to utf8 (for storage or printing)
345    if ($this->cleanEntry) {$row=$this->convertEntry($row);}
346    //
347
348
349    /**
350     * Bibtex-specific types not defined in STYLEMAPBIBTEX
351     */
352
353    if(!$this->type)
354      {
355	if($type == 'mastersthesis')
356	  {
357	    $type = 'thesis';
358	    $row['type'] = "Master's Dissertation";
359	  }
360	if($type == 'phdthesis')
361	  {
362	    $type = 'thesis';
363	    $row['type'] = "PhD Thesis";
364	  }
365	else if($type == 'booklet')
366	  $type = 'miscellaneous';
367	else if($type == 'conference')
368	  $type = 'proceedings_article';
369	else if($type == 'incollection')
370	  $type = 'book_article';
371	else if($type == 'manual')
372	  $type = 'report';
373      }
374    /**
375     * 'article' could be journal, newspaper or magazine article
376     */
377    else if($type == 'journal_article')
378      {
379	if(array_key_exists('month', $row) && array_key_exists('date', $this->styleMap->$type))
380	  {
381	    list($startMonth, $startDay, $endMonth, $endDay) = $parseDate->init($row['month']);
382	    if($startDay)
383	      $type = 'newspaper_article';
384	    else if($startMonth)
385	      $type = 'magazine_article';
386	    $this->formatDate($startDay, $startMonth, $endDay, $endMonth);
387	  }
388	else
389	  $type = 'journal_article';
390      }
391    /**
392     * Is this a web article?
393     */
394    else if(($type == 'miscellaneous') && array_key_exists('howpublished', $row))
395      {
396	if(preg_match("#^\\\url{(.*://.*)}#", $row['howpublished'], $match))
397	  {
398	    $row['URL'] = $match[1];
399	    $type = 'web_article';
400	  }
401      }
402    $this->type = $type;
403    if(array_key_exists('creator1', $row) && $row['creator1'] &&
404       array_key_exists('creator1', $this->styleMap->$type))
405      {
406	$creators = $parseCreator->parse($row['creator1']);
407	foreach($creators as $cArray)
408	  {
409	    $temp[] = array(
410			    'surname'	=>	trim($cArray[2]),
411			    'firstname'	=>	trim($cArray[0]),
412			    'initials'	=>	trim($cArray[1]),
413			    'prefix'	=>	trim($cArray[3]),
414			    );
415	  }
416	$this->formatNames($temp, 'creator1');
417	unset($temp);
418      }
419    if(array_key_exists('creator2', $row) && $row['creator2'] &&
420       array_key_exists('creator2', $this->styleMap->$type))
421      {
422	$creators = $parseCreator->parse($row['creator2']);
423	foreach($creators as $cArray)
424	  {
425	    $temp[] = array(
426			    'surname'	=>	trim($cArray[2]),
427			    'firstname'	=>	trim($cArray[0]),
428			    'initials'	=>	trim($cArray[1]),
429			    'prefix'	=>	trim($cArray[3]),
430			    );
431	  }
432	$this->formatNames($temp, 'creator2');
433      }
434    if(array_key_exists('pages', $row) && array_key_exists('pages', $this->styleMap->$type))
435      {
436	list($start, $end) = $parsePages->init($row['pages']);
437	$this->formatPages(trim($start), trim($end));
438      }
439    $this->formatTitle($row['title'], "{", "}");
440    return array($type, $row);
441  }
442  /**
443   * Map the $item array against the style array ($this->$type) for this resource type and produce a string ready to be
444   * formatted for bold, italics etc.
445   *
446   * @author	Mark Grimshaw
447   * @version	1
448   *
449   * @param	$template	If called from CITEFORMAT, this is the array of template elements.
450   * @return	string ready for printing to the output medium.
451   */
452  function map($template = FALSE)
453  {
454    /**
455     * Output medium:
456     * 'html', 'rtf', or 'plain'
457     */
458    include_once($this->dir . "format/EXPORTFILTER.php");
459    $this->export = new EXPORTFILTER($this, $this->output);
460    if($template)
461      {
462	$this->citation = $template;
463	$this->type = 'citation';
464      }
465    $type = $this->type;
466    $ultimate = '';
467    $index = 0;
468    $previousFieldExists = $nextFieldExists = TRUE;
469    if(array_key_exists('independent', $this->$type))
470      $independent = $this->{$type}['independent'];
471    /**
472     * For dependency on next field, we must grab array keys of $this->$type, shift the first element then, in the loop,
473     * check each element exists in $item.  If it doesn't, $nextFieldExists is set to FALSE
474     */
475    $checkPost = array_keys($this->$type);
476    array_shift($checkPost);
477    foreach($this->$type as $key => $value)
478      {
479	if($key == 'ultimate')
480	  {
481	    $ultimate = $value;
482	    continue;
483	  }
484	if(!array_key_exists($key, $this->item) || !$this->item[$key])
485	  {
486	    $keyNotExists[] = $index;
487	    $index++;
488	    array_shift($checkPost);
489	    $previousFieldExists = FALSE;
490	    continue;
491	  }
492	$checkPostShift = array_shift($checkPost);
493	if(!array_key_exists($checkPostShift, $this->item) || !$this->item[$checkPostShift])
494	  $nextFieldExists = FALSE;
495	$pre = array_key_exists('pre', $value) ? $value['pre'] : '';
496	$post = array_key_exists('post', $value) ? $value['post'] : '';
497	/**
498	 * Deal with __DEPENDENT_ON_PREVIOUS_FIELD__ for characters dependent on previous field's existence and
499	 * __DEPENDENT_ON_NEXT_FIELD__ for characters dependent on the next field's existence
500	 */
501	if($previousFieldExists && array_key_exists('dependentPre', $value))
502	  $pre = preg_replace("/__DEPENDENT_ON_PREVIOUS_FIELD__/",
503			      $value['dependentPre'], $pre);
504	else if(array_key_exists('dependentPreAlternative', $value))
505	  $pre = preg_replace("/__DEPENDENT_ON_PREVIOUS_FIELD__/",
506			      $value['dependentPreAlternative'], $pre);
507	else
508	  $pre = preg_replace("/__DEPENDENT_ON_PREVIOUS_FIELD__/", '', $pre);
509	if($nextFieldExists && array_key_exists('dependentPost', $value))
510	  $post = str_replace("__DEPENDENT_ON_NEXT_FIELD__",
511			      $value['dependentPost'], $post);
512	else if(array_key_exists('dependentPostAlternative', $value))
513	  $post = preg_replace("/__DEPENDENT_ON_NEXT_FIELD__/",
514			       $value['dependentPostAlternative'], $post);
515	else
516	  $post = preg_replace("/__DEPENDENT_ON_NEXT_FIELD__/", '', $post);
517	/**
518	 * Deal with __SINGULAR_PLURAL__ for creator lists and pages
519	 */			if($styleKey = array_search($key, $this->styleMap->$type))
520	  $pluralKey = $styleKey . "_plural";
521	if(isset($this->$pluralKey) && $this->$pluralKey) // plural alternative for this key
522	  {
523	    $pre = array_key_exists('plural', $value) ?
524	      preg_replace("/__SINGULAR_PLURAL__/", $value['plural'], $pre) : $pre;
525	    $post = array_key_exists('plural', $value) ?
526	      preg_replace("/__SINGULAR_PLURAL__/", $value['plural'], $post) : $post;
527	  }
528	else if(isset($this->$pluralKey)) // singular alternative for this key
529	  {
530	    $pre = array_key_exists('singular', $value) ?
531	      preg_replace("/__SINGULAR_PLURAL__/", $value['singular'], $pre) : $pre;
532	    $post = array_key_exists('singular', $value) ?
533	      preg_replace("/__SINGULAR_PLURAL__/", $value['singular'], $post) : $post;
534	  }
535	/**
536	 * Make sure we don't have duplicate punctuation characters
537	 */			$lastPre = substr($post, -1);
538	$firstItem = substr($this->item[$key], 0, 1);
539	if($firstItem === $lastPre)
540	  $this->item[$key] = substr($this->item[$key], 1);
541	$firstPost = substr($post, 0, 1);
542	$lastItem = substr($this->item[$key], -1);
543	//			if(preg_match("/\.|,|;|:\?!/", $lastItem) && preg_match("/\.|,|;|:|\?|!/", $firstPost))
544	if(preg_match("/\.|,|;|:\?!/", $lastItem) && ($firstPost == $lastItem))
545	  $post = substr($post, 1); // take a guess at removing first character of $post
546	/**
547	 * Strip backticks used in template
548	 */
549	$pre = str_replace("`", '', $pre);
550	$post = str_replace("`", '', $post);
551	$pre = ($this->output == 'html') ? $this->utf8->utf8_htmlspecialchars($pre) : $pre;
552	$post = ($this->output == 'html') ? $this->utf8->utf8_htmlspecialchars($post) : $post;
553	if($this->item[$key])
554	  $itemArray[$index] = $pre . $this->item[$key] . $post;
555	$previousFieldExists = $nextFieldExists = TRUE;
556	$index++;
557      }
558    /**
559     * Check for independent characters.  These (should) come in pairs.
560     */		if(isset($independent))
561      {
562	$independentKeys = array_keys($independent);
563	while($independent)
564	  {
565	    $preAlternative = $postAlternative = FALSE;
566	    $startFound = $endFound = FALSE;
567	    $pre = array_shift($independent);
568	    $post = array_shift($independent);
569	    if(preg_match("/%(.*)%(.*)%|%(.*)%/U", $pre, $dependent))
570	      {
571		if(sizeof($dependent) == 4)
572		  $pre = $dependent[3];
573		else
574		  {
575		    $pre = $dependent[1];
576		    $preAlternative = $dependent[2];
577		  }
578	      }
579	    if(preg_match("/%(.*)%(.*)%|%(.*)%/U", $post, $dependent))
580	      {
581		if(sizeof($dependent) == 4)
582		  $post = $dependent[3];
583		else
584		  {
585		    $post = $dependent[1];
586		    $postAlternative = $dependent[2];
587		  }
588	      }
589	    /**
590	     * Strip backticks used in template
591	     */
592	    $preAlternative = str_replace("`", '', $preAlternative);
593	    $postAlternative = str_replace("`", '', $postAlternative);
594	    $firstKey = array_shift($independentKeys);
595	    $secondKey = array_shift($independentKeys);
596	    for($index = $firstKey; $index <= $secondKey; $index++)
597	      {
598		if(array_key_exists($index, $itemArray))
599		  {
600		    $startFound = $index;
601		    break;
602		  }
603	      }
604	    for($index = $secondKey; $index >= $firstKey; $index--)
605	      {
606		if(array_key_exists($index, $itemArray))
607		  {
608		    $endFound = $index;
609		    break;
610		  }
611	      }
612	    if(($startFound !== FALSE) && ($endFound !== FALSE)) // intervening fields found
613	      {
614		$itemArray[$startFound] = $pre . $itemArray[$startFound];
615		$itemArray[$endFound] = $itemArray[$endFound] . $post;
616	      }
617	    else // intervening fields not found - do we have an alternative?
618	      {
619		if(array_key_exists($firstKey - 1, $itemArray) && $preAlternative)
620		  $itemArray[$firstKey - 1] .= $preAlternative;
621		if(array_key_exists($secondKey + 1, $itemArray) && $postAlternative)
622		  $itemArray[$secondKey + 1] = $postAlternative .
623		    $itemArray[$secondKey + 1];
624	      }
625	  }
626      }
627    $pString = join('', $itemArray);
628    /**
629     * if last character is punctuation (which it may be with missing fields etc.), and $ultimate is also
630     * punctuation, remove last character.
631     */		if(isset($ultimate) && $ultimate)
632      {
633	$last = substr(trim($pString), -1);
634	/**
635	 * Don't do ';' in case last element is URL with &gt; ...!
636	 */
637	if(preg_match("/^\.|^,||^:^\?^\!/", $ultimate) && preg_match("/\.|,|:|\?|!/", $last))
638	  $pString = substr(trim($pString), 0, -1);
639      }
640    // If $this->editorSwitch, we have altered $this->$bibformat->$type so need to reload styles
641    if($this->editorSwitch)
642      {
643	$this->restoreTypes();
644	$this->editorSwitch = FALSE;
645      }
646    return $this->export->format(trim($pString) . $ultimate);
647  }
648  /**
649   * Format creator name lists (authors, editors, etc.)
650   *
651   * @author	Mark Grimshaw
652   * @version	1
653   *
654   * @param	$creators	Multi-associative array of creator names e.g. this array might be of
655   * the primary authors:
656   * <pre>
657   *	array([0] => array(['surname'] => 'Grimshaw', ['firstname'] => Mark, ['initials'] => 'N', ['prefix'] => ),
658   *	   [1] => array(['surname'] => 'Witt', ['firstname'] => Jan, ['initials'] => , ['prefix'] => 'de'))
659   * </pre>
660   * @param	$nameType	'creator1', 'creator2' etc.  If $nameType == 'citation', this method is called
661   * from CITEFORMAT for formatting citation creators in which case we expect the 3rd parameter $citation.
662   * @param	$citation	If called from CITEFORMAT, this is the array of citation stylings.
663   * @return	Optional if $nameType == 'citation': formatted string of all creator names in the input array.
664   */
665  function formatNames($creators, $nameType, $citation = FALSE)
666  {
667    $style = $citation ? $citation : $this->style;
668    $first = TRUE;
669    /**
670     * Set default plural behaviour for creator lists
671     */
672    $pluralKey = $nameType . "_plural";
673    $this->$pluralKey = FALSE;
674    //		$this->creator1_plural = $this->creator2_plural =
675    //			$this->creator3_plural = $this->creator4_plural = $this->creator5_plural = FALSE;
676    /**
677     * Citation creators
678     */
679    if($nameType == 'citation')
680      {
681	$limit = 'creatorListLimit';
682	$moreThan = 'creatorListMore';
683	$abbreviation = 'creatorListAbbreviation';
684	$initialsStyle = 'creatorInitials';
685	$firstNameInitial = 'creatorFirstName';
686	$delimitTwo = 'twoCreatorsSep';
687	$delimitFirstBetween = 'creatorSepFirstBetween';
688	$delimitNextBetween = 'creatorSepNextBetween';
689	$delimitLast = 'creatorSepNextLast';
690	$uppercase = 'creatorUppercase';
691	$italics = 'creatorListAbbreviationItalic';
692	if($first)
693	  $nameStyle = 'creatorStyle';
694	else
695	  $nameStyle = 'creatorOtherStyle';
696      }
697    /**
698     * Primary creator
699     */
700    else if($nameType == 'creator1')
701      {
702	$limit = 'primaryCreatorListLimit';
703	$moreThan = 'primaryCreatorListMore';
704	$abbreviation = 'primaryCreatorListAbbreviation';
705	$initialsStyle = 'primaryCreatorInitials';
706	$firstNameInitial = 'primaryCreatorFirstName';
707	$delimitTwo = 'primaryTwoCreatorsSep';
708	$delimitFirstBetween = 'primaryCreatorSepFirstBetween';
709	$delimitNextBetween = 'primaryCreatorSepNextBetween';
710	$delimitLast = 'primaryCreatorSepNextLast';
711	$uppercase = 'primaryCreatorUppercase';
712	$italics = 'primaryCreatorListAbbreviationItalic';
713	if($first)
714	  $nameStyle = 'primaryCreatorFirstStyle';
715	else
716	  $nameStyle = 'primaryCreatorOtherStyle';
717      }
718    else
719      {
720	$limit = 'otherCreatorListLimit';
721	$moreThan = 'otherCreatorListMore';
722	$abbreviation = 'otherCreatorListAbbreviation';
723	$initialsStyle = 'otherCreatorInitials';
724	$firstNameInitial = 'otherCreatorFirstName';
725	$delimitTwo = 'otherTwoCreatorsSep';
726	$delimitFirstBetween = 'otherCreatorSepFirstBetween';
727	$delimitNextBetween = 'otherCreatorSepNextBetween';
728	$delimitLast = 'otherCreatorSepNextLast';
729	$uppercase = 'otherCreatorUppercase';
730	$italics = 'otherCreatorListAbbreviationItalic';
731	if($first)
732	  $nameStyle = 'otherCreatorFirstStyle';
733	else
734	  $nameStyle = 'otherCreatorOtherStyle';
735      }
736    $type = $this->type;
737    foreach($creators as $creator)
738      {
739	$firstName = trim($this->checkInitials($creator, $style[$initialsStyle],
740					       $style[$firstNameInitial]));
741	$prefix = $creator['prefix'] ? trim(stripslashes($creator['prefix'])) . ' ' : '';
742	if($style[$nameStyle] == 0) // Joe Bloggs
743	  {
744	    $nameString = $firstName . ' ' .
745	      $prefix .
746	      stripslashes($creator['surname']);
747	  }
748	else if($style[$nameStyle] == 1) // Bloggs, Joe
749	  {
750	    $prefixDelimit = $firstName ? ', ' : '';
751	    $nameString =
752	      stripslashes($creator['prefix']) . ' ' .
753	      stripslashes($creator['surname']) . $prefixDelimit .
754	      $firstName;
755	  }
756	else if($style[$nameStyle] == 2) // Bloggs Joe
757	  {
758	    $nameString =
759	      stripslashes($creator['prefix']) . ' ' .
760	      stripslashes($creator['surname']) . ' ' .
761	      $firstName;
762	  }
763	else // Last name only
764	  {
765	    $nameString =
766	      stripslashes($creator['prefix']) . ' ' .
767	      stripslashes($creator['surname']);
768	  }
769	if(isset($style[$uppercase]))
770	  $nameString = $this->utf8->utf8_strtoupper($nameString);
771	$cArray[] = trim($nameString);
772	$first = FALSE;
773      }
774    /**
775     * Keep only some elements in array if we've exceeded $moreThan
776     */
777    $etAl = FALSE;
778    if($style[$limit] && (sizeof($cArray) > $style[$moreThan]))
779      {
780	array_splice($cArray, $style[$limit]);
781	if(isset($style[$italics]))
782	  $etAl = "[i]" . $style[$abbreviation] . "[/i]";
783	else
784	  $etAl = $style[$abbreviation];
785      }
786    /**
787     * add delimiters
788     */
789    if(sizeof($cArray) > 1)
790      {
791	if(sizeof($cArray) == 2)
792	  $cArray[0] .= $style[$delimitTwo];
793	else
794	  {
795	    for($index = 0; $index < (sizeof($cArray) - 2); $index++)
796	      {
797		if(!$index)
798		  $cArray[$index] .= $style[$delimitFirstBetween];
799		else
800		  $cArray[$index] .= $style[$delimitNextBetween];
801	      }
802	    $cArray[sizeof($cArray) - 2] .= $style[$delimitLast];
803	  }
804      }
805    /**
806     * If sizeof of $cArray > 1 or $etAl != FALSE, set this $nameType_plural to TRUE
807     */
808    if((sizeof($cArray) > 1) || $etAl)
809      {
810	$pluralKey = $nameType . "_plural";
811	$this->$pluralKey = TRUE;
812      }
813    /**
814     * Finally flatten array
815     */
816    if($etAl)
817      $pString = implode('', $cArray) . $etAl;
818    else
819      $pString = implode('', $cArray);
820    /**
821     * Check for repeating primary creator list in subsequent bibliographic item.
822     */
823    if($nameType == 'creator1')
824      {
825	$tempString = $pString;
826	if(($style['primaryCreatorRepeat'] == 2) && ($this->previousCreator == $pString))
827	  $pString = $style['primaryCreatorRepeatString'];
828	else if(($style['primaryCreatorRepeat'] == 1) &&
829		($this->previousCreator == $pString))
830	  $pString = ''; // don't print creator list
831	$this->previousCreator = $tempString;
832      }
833    else if($nameType == 'citation')
834      return $pString;
835    $this->item[$this->styleMap->{$type}[$nameType]] = $pString;
836  }
837  /**
838   * Handle initials.
839   * @see formatNames()
840   *
841   * @author	Mark Grimshaw
842   * @version	1
843   *
844   * @param	$creator	Associative array of creator name e.g.
845   * <pre>
846   *	array(['surname'] => 'Grimshaw', ['firstname'] => Mark, ['initials'] => 'M N G', ['prefix'] => ))
847   * </pre>
848   * Initials must be space-delimited.
849   *
850   * @param	$initialsStyle
851   * @param	$firstNameInitial
852   * @return	Formatted string of initials.
853   */
854  function checkInitials(&$creator, $initialsStyle, $firstNameInitial)
855  {
856    /**
857     * Format firstname
858     */
859    if($creator['firstname'] && !$firstNameInitial) // Full name
860      $firstName = stripslashes($creator['firstname']);
861    else if($creator['firstname']) // Initial only of first name.  'firstname' field may actually have several 'firstnames'
862      {
863	$fn = split(" ", stripslashes($creator['firstname']));
864	$firstTime = TRUE;
865	foreach($fn as $name)
866	  {
867	    if($firstTime)
868	    {
869		    // May be the first name is a hyphenated name
870		    // We separate each part of the name separated by a -
871		    $fn2 = split("-", trim($name));
872		    $firstNameInitialMake = "";
873		    foreach($fn2 as $nameparts)
874		    {
875			    if ($firstNameInitialMake != "")
876			    {
877				    $firstNameInitialMake .= "-";
878			    }
879			    $firstNameInitialMake .= $this->utf8->utf8_strtoupper($this->utf8->utf8_substr($nameparts, 0, 1));
880			    $firstTime = FALSE;
881		    }
882	      }
883	    else
884	      $initials[] = $this->utf8->utf8_strtoupper($this->utf8->utf8_substr(trim($name), 0, 1));
885	  }
886	if(isset($initials))
887	  {
888	    if($creator['initials'])
889	      $creator['initials'] = join(" " , $initials) . ' ' . $creator['initials'];
890	    else
891	      $creator['initials'] = join(" " , $initials);
892	  }
893      }
894    /**
895     * Initials are stored as space-delimited characters.
896     * If no initials, return just the firstname or its initial in the correct format.
897     */
898    if(!$creator['initials'])
899      {
900	if(isset($firstName))	// full first name only
901	  return $firstName;
902	if(isset($firstNameInitialMake) && $initialsStyle > 1) // First name initial with no '.'
903	  return $firstNameInitialMake;
904	if(isset($firstNameInitialMake)) // First name initial with  '.'
905	{
906		// If the name is hyphaned, we have to add a . after each initial.
907		$fn2 = split("-", $firstNameInitialMake);
908		$firstNameInitialMakeResult = "";
909		foreach($fn2 as $initial)
910		{
911			if ($firstNameInitialMakeResult != "")
912			{
913				$firstNameInitialMakeResult .= "-";
914			}
915			$firstNameInitialMakeResult .= $initial . ".";
916		}
917	  return $firstNameInitialMakeResult;
918  }
919	return ''; // nothing here
920      }
921    $initialsArray = explode(' ', $creator['initials']);
922    /**
923     * If firstname is initial only, prepend to array
924     */
925    if(isset($firstNameInitialMake))
926      array_unshift($initialsArray, $firstNameInitialMake);
927    if($initialsStyle == 0) // 'T. U. '
928      $initials = implode('. ', $initialsArray) . '.';
929    else if($initialsStyle == 1) // 'T.U.'
930      $initials = implode('.', $initialsArray) . '.';
931    else if($initialsStyle == 2) // 'T U '
932      $initials = implode(' ', $initialsArray);
933    else // 'TU '
934      $initials = implode('', $initialsArray);
935    /**
936     * If we have a full first name, prepend it to $initials.
937     */
938    if(isset($firstName))
939      return ($firstName . ' ' . $initials);
940    return $initials;
941  }
942  /**
943   * Add an item to $this->item array
944   *
945   * @author	Mark Grimshaw
946   * @version	1
947   *
948   * @param	$item		The item to be added.
949   * @param	$fieldName	The database fieldName of the item to be added
950   */
951  function addItem($item, $fieldName)
952  {
953    $type = $this->type;
954    if($item === FALSE)
955      return;
956    /**
957     * This item may already exist (e.g. edition field for WIKINDX)
958     */
959    if(isset($this->item) && array_key_exists($this->styleMap->{$type}[$fieldName], $this->item))
960      return FALSE;
961    $this->item[$this->styleMap->{$type}[$fieldName]] = $item;
962  }
963  /**
964   * Add all remaining items to $this->item array
965   *
966   * @author	Mark Grimshaw
967   * @version	1
968   *
969   * @param	$row		The items to be added.
970   */
971  function addAllOtherItems($row)
972  {
973    $type = $this->type;
974    foreach($row as $field => $value)
975      {
976	if(array_key_exists($field, $this->styleMap->$type) &&
977	   !array_key_exists($this->styleMap->{$type}[$field], $this->item))
978	  $this->addItem($row[$field], $field);
979      }
980  }
981  /**
982   * Format a title.  Anything enclosed in $delimitLeft...$delimitRight is to be left unchanged
983   *
984   * @author	Mark Grimshaw
985   * @version	1
986   *
987   * @param	$pString	Raw title string.
988   * @param	$delimitLeft
989   * @param	$delimitRight
990   * @return	Formatted title string.
991   */
992  function formatTitle($pString, $delimitLeft = FALSE, $delimitRight = FALSE)
993  {
994    if(!$delimitLeft)
995      $delimitLeft = '{';
996    if(!$delimitRight)
997      $delimitRight = '}';
998    $delimitLeft = preg_quote($delimitLeft);
999    $delimitRight = preg_quote($delimitRight);
1000    $match = "/" . $delimitLeft . "/";
1001    $type = $this->type;
1002    if(!array_key_exists('title', $this->styleMap->$type))
1003      $this->item[$this->styleMap->{$type}['title']] = '';
1004    /**
1005     * '0' == 'Osbib Bibliographic Formatting'
1006     * '1' == 'Osbib bibliographic formatting'
1007     */
1008    if($this->style['titleCapitalization'])
1009      {
1010	// Something here (preg_split probably) interferes with UTF-8 encoding (data is stored in
1011	// the database as UTF-8 as long as web browser charset == UTF-8).
1012	// So first decode then encode back to UTF-8 at end.
1013	// There is a 'u' UTF-8 parameter for preg_xxx but it doesn't work.
1014	$pString = $this->utf8->decodeUtf8($pString);
1015	$newString = '';
1016	while(preg_match($match, $pString))
1017	  {
1018	    $array = preg_split("/(.*)$delimitLeft(.*)$delimitRight(.*)/U",
1019				$pString, 2, PREG_SPLIT_DELIM_CAPTURE);
1020	    /**
1021	     * in case user has input {..} incorrectly
1022	     */
1023	    if(sizeof($array) == 1)
1024	      break;
1025	    $newString .= $this->utf8->utf8_strtolower($this->utf8->encodeUtf8($array[1])) . $array[2];
1026	    $pString = $array[4];
1027	  }
1028	$newString .= $this->utf8->utf8_strtolower($this->utf8->encodeUtf8($pString));
1029      }
1030    $pString = isset($newString) ? $newString : $pString;
1031    $title = $this->utf8->encodeUtf8($this->utf8->utf8_ucfirst(trim($pString)));
1032    $this->item[$this->styleMap->{$type}['title']] =
1033      ($this->output == 'html') ? $this->utf8->utf8_htmlspecialchars($title) : $title;
1034  }
1035  /**
1036   * Format pages.
1037   * $this->style['pageFormat']:
1038   * 0 == 132-9
1039   * 1 == 132-39
1040   * 2 == 132-139
1041   *
1042   * @author	Mark Grimshaw
1043   * @version	1
1044   *
1045   * @param	$start		Page start.
1046   * @param	$end		Page end.
1047   * @param	$citation	If called from CITEFORMAT, this is the array of citation stylings.
1048   * @return	string of pages.
1049   */
1050  function formatPages($start, $end = FALSE, $citation = FALSE)
1051  {
1052    $type = $this->type;
1053    $style = $citation ? $citation : $this->style;
1054    /**
1055     * Set default plural behaviour for pages
1056     */
1057    $this->pages_plural = FALSE;
1058    /**
1059     * If no page end, return just $start;
1060     */
1061    if(!$end)
1062      {
1063	$this->item[$this->styleMap->{$type}['pages']] = $start;
1064	return;
1065      }
1066    /**
1067     * Pages may be in roman numeral format etc.  Return unchanged
1068     */
1069    if(!is_numeric($start))
1070      {
1071	$this->item[$this->styleMap->{$type}['pages']] = $start . '-' . $end;
1072	return;
1073      }
1074    /**
1075     * We have multiple pages...
1076     */
1077    $this->pages_plural = TRUE;
1078    /**
1079     * They've done something wrong so give them back exactly what they entered
1080     */
1081    if(($end <= $start) || (strlen($end) < strlen($start)))
1082      {
1083	$this->item[$this->styleMap->{$type}['pages']] = $start . '-' . $end;
1084	return;
1085      }
1086    else if($style['pageFormat'] == 2)
1087      {
1088	$this->item[$this->styleMap->{$type}['pages']] = $start . '-' . $end;
1089	return;
1090      }
1091    else
1092      {
1093	/**
1094	 * We assume page numbers are not into the 10,000 range - if so, return the complete pages
1095	 */
1096	if(strlen($start) <= 4)
1097	  {
1098	    $startArray = preg_split('//', $start);
1099	    array_shift($startArray); // always an empty element at start?
1100	    array_pop($startArray); // always an empty array element at end?
1101	    if($style['pageFormat'] == 0)
1102	      {
1103		array_pop($startArray);
1104		$endPage = substr($end, -1);
1105		$index = -2;
1106	      }
1107	    else
1108	      {
1109		array_pop($startArray);
1110		array_pop($startArray);
1111		$endPage = substr($end, -2);
1112		$index = -3;
1113	      }
1114	    while(!empty($startArray))
1115	      {
1116		$startPop = array_pop($startArray);
1117		$endSub = substr($end, $index--, 1);
1118		if($endSub == $startPop)
1119		  {
1120		    $this->item[$this->styleMap->{$type}['pages']]
1121		      = $start . '-' . $endPage;
1122		    return;
1123		  }
1124		if($endSub > $startPop)
1125		  $endPage = $endSub . $endPage;
1126	      }
1127	  }
1128	else
1129	  {
1130	    $this->item[$this->styleMap->{$type}['pages']] = $start . '-' . $end;
1131	    return;
1132	  }
1133      }
1134    /**
1135     * We should never reach here - in case we do, give back complete range so that something at least is printed
1136     */
1137    $this->item[$this->styleMap->{$type}['pages']] = $start . '-' . $end;
1138  }
1139  /**
1140   * Format runningTime for film/broadcast
1141   *
1142   * @author	Mark Grimshaw
1143   * @version	1
1144   *
1145   * @param	$minutes
1146   * @param	$hours
1147   */
1148  function formatRunningTime($minutes, $hours)
1149  {
1150    $type = $this->type;
1151    if($this->style['runningTimeFormat'] == 0) // 3'45"
1152      {
1153	if(isset($minutes) && $minutes)
1154	  {
1155	    if($minutes < 10)
1156	      $minutes = '0' . $minutes;
1157	    $runningTime = $hours . "'" . $minutes . "\"";
1158	  }
1159	else
1160	  $runningTime = $hours . "'00\"";
1161      }
1162    else if($this->style['runningTimeFormat'] == 1) // 3:45
1163      {
1164	if(isset($minutes) && $minutes)
1165	  {
1166	    if($minutes < 10)
1167	      $minutes = '0' . $minutes;
1168	    $runningTime = $hours . ":" . $minutes;
1169	  }
1170	else
1171	  $runningTime = $hours . ":00";
1172      }
1173    else if($this->style['runningTimeFormat'] == 1) // 3,45
1174      {
1175	if(isset($minutes) && $minutes)
1176	  {
1177	    if($minutes < 10)
1178	      $minutes = '0' . $minutes;
1179	    $runningTime = $hours . "," . $minutes;
1180	  }
1181	else
1182	  $runningTime = $hours . ",00";
1183      }
1184    else if($this->style['runningTimeFormat'] == 3) // 3 hours, 45 minutes
1185      {
1186	$hours = ($hours == 1) ? $hours . " hour" : $hours . " hours";
1187	if(isset($minutes) && $minutes)
1188	  {
1189	    $minutes = ($minutes == 1) ? $minutes . " minute" : $minutes . " minutes";
1190	    $runningTime = $hours . ", " . $minutes;
1191	  }
1192	else
1193	  $runningTime = $hours;
1194      }
1195    else if($this->style['runningTimeFormat'] == 4) // 3 hours and 45 minutes
1196      {
1197	$hours = ($hours == 1) ? $hours . " hour" : $hours . " hours";
1198	if(isset($minutes) && $minutes)
1199	  {
1200	    $minutes = ($minutes == 1) ? $minutes . " minute" : $minutes . " minutes";
1201	    $runningTime = $hours . " and " . $minutes;
1202	  }
1203	else
1204	  $runningTime = $hours;
1205      }
1206    $this->item[$this->styleMap->{$type}['runningTime']] = $runningTime;
1207  }
1208  /**
1209   * Format date
1210   *
1211   * @author	Mark Grimshaw
1212   * @version	2
1213   *
1214   * @param	INT $startDay
1215   * @param	INT $startMonth
1216   * @param	INT $endDay
1217   * @param	INT $sendMonth
1218   */
1219  function formatDate($startDay, $startMonth, $endDay, $endMonth)
1220  {
1221    $type = $this->type;
1222    if($startDay !== FALSE)
1223      {
1224	if($this->style['dayFormat']) // e.g. 10th
1225	  $startDay = $this->cardinalToOrdinal($startDay);
1226	else if($startDay < 10)
1227	  $startDay = '0' . $startDay;
1228      }
1229    if($endDay !== FALSE)
1230      {
1231	if($this->style['dayFormat']) // e.g. 10th
1232	  $endDay = $this->cardinalToOrdinal($endDay);
1233	else if($endDay < 10)
1234	  $endDay = '0' . $endDay;
1235      }
1236    if($this->style['monthFormat'] == 1) // Full month name
1237      $monthArray = $this->longMonth;
1238    else if($this->style['monthFormat'] == 2) // User-defined
1239      {
1240	for($i = 1; $i <= 12; $i++)
1241	  $monthArray[$i] = $this->style["userMonth_$i"];
1242      }
1243    else // Short month name
1244      $monthArray = $this->shortMonth;
1245    if($startMonth !== FALSE)
1246      $startMonth = $monthArray[$startMonth];
1247    if($endMonth !== FALSE)
1248      $endMonth = $monthArray[$endMonth];
1249    if(!$endMonth)
1250      {
1251	if($this->style['dateFormat']) // Order == Month Day
1252	  {
1253	    $startDay = ($startDay === FALSE) ? '' : ' ' . $startDay;
1254	    $date = $startMonth . $startDay;
1255	  }
1256	else // Order == Day Month
1257	  {
1258	    $startDay = ($startDay === FALSE) ? '' : $startDay . ' ';
1259	    $date = $startDay . $startMonth;
1260	  }
1261      }
1262    else // date range
1263      {
1264	if(!$startDay)
1265	  $delimit = $this->style['dateRangeDelimit2'];
1266	else
1267	  $delimit = $this->style['dateRangeDelimit1'];
1268	if(($endMonth !== FALSE) && ($startMonth == $endMonth) && ($this->style['dateRangeSameMonth'] == 1))
1269	  {
1270	    $endMonth = FALSE;
1271	    if(!$endDay)
1272	      $delimit = FALSE;
1273	  }
1274	if($this->style['dateFormat']) // Order == Month Day
1275	  {
1276	    $startDay = ($startDay === FALSE) ? '' : ' ' . $startDay;
1277	    $startDate = $startMonth . $startDay;
1278	    if($endMonth)
1279	      $endDate = $endMonth . $endDay = ($endDay === FALSE) ? '' : ' ' . $endDay;
1280	    else
1281	      $endDate = $endDay;
1282	  }
1283	else // Order == Day Month
1284	  {
1285	    if($endMonth)
1286	      {
1287		$startDate = $startDay . ' ' . $startMonth;
1288		$endDate = $endDay = ($endDay === FALSE) ? '' : $endDay . ' ';
1289		$endDate .= $endMonth;
1290	      }
1291	    else
1292	      {
1293		$startDate = $startDay;
1294		$endDate = ($endDay === FALSE) ? ' ' : $endDay . ' ';
1295		$endDate .= $startMonth;
1296	      }
1297	  }
1298	$date = $startDate . $delimit . $endDate;
1299      }
1300    $this->item[$this->styleMap->{$type}['date']] = $date;
1301  }
1302  /**
1303   * Format edition
1304   *
1305   * @author	Mark Grimshaw
1306   * @version	1
1307   *
1308   * @param	INT $edition
1309   * @return	string of edition.
1310   */
1311  function formatEdition($edition)
1312  {
1313    $type = $this->type;
1314    if(!is_numeric($edition))
1315      $edition = ($this->output == 'html') ? $this->utf8->utf8_htmlspecialchars($edition) : $edition;
1316    else if($this->style['editionFormat']) // 10th
1317      $edition = ($this->output == 'html') ? $this->utf8->utf8_htmlspecialchars($this->cardinalToOrdinal($edition))
1318	: $this->cardinalToOrdinal($edition);
1319    $this->item[$this->styleMap->{$type}[array_search('edition', $this->styleMap->$type)]] = $edition;
1320  }
1321  /**
1322   * Create ordinal number from cardinal
1323   *
1324   * @author	Mark Grimshaw
1325   * @version	1
1326   *
1327   * @param	$cardinal
1328   * @return	$ordinal
1329   */
1330  function cardinalToOrdinal($cardinal)
1331  {
1332    $modulo = $cardinal % 100;
1333    if(($modulo == 11) || ($modulo == 12) || ($modulo == 13))
1334      return $cardinal . 'th';
1335    $modulo = $cardinal % 10;
1336    if(($modulo >= 4) || !$modulo)
1337      return $cardinal . 'th';
1338    if($modulo == 1)
1339      return $cardinal . 'st';
1340    if($modulo == 2)
1341      return $cardinal . 'nd';
1342    if($modulo == 3)
1343      return $cardinal . 'rd';
1344  }
1345  /**
1346   * Month arrays
1347   * @author	Mark Grimshaw
1348   * @version	1
1349   */
1350  function loadArrays()
1351  {
1352    $this->longMonth = array(
1353			     1	=>	'January',
1354			     2	=>	'February',
1355			     3	=>	'March',
1356			     4	=>	'April',
1357			     5	=>	'May',
1358			     6	=>	'June',
1359			     7	=>	'July',
1360			     8	=>	'August',
1361			     9	=>	'September',
1362			     10	=>	'October',
1363			     11	=>	'November',
1364			     12	=>	'December',
1365			     );
1366    $this->shortMonth = array(
1367			      1	=>	'Jan',
1368			      2	=>	'Feb',
1369			      3	=>	'Mar',
1370			      4	=>	'Apr',
1371			      5	=>	'May',
1372			      6	=>	'Jun',
1373			      7	=>	'Jul',
1374			      8	=>	'Aug',
1375			      9	=>	'Sep',
1376			      10	=>	'Oct',
1377			      11	=>	'Nov',
1378			      12	=>	'Dec',
1379			      );
1380  }
1381
1382
1383
1384/*
1385 * convertEntry - convert any laTeX code and convert to UTF-8 ready
1386 for storing in the database
1387 *
1388 * @author Mark Grimshaw, modified by Christophe Ambroise 26/10/2003
1389 * @param array $entry - a bibtex entry
1390 * @return $entry converted to utf8
1391
1392 */
1393function convertEntry($entry)
1394{
1395  $this->config = new BIBTEXCONFIG();
1396  $this->config->bibtex();
1397
1398  // Construction of the transformation filter
1399  foreach($this->config->bibtexSpCh as $key => $value)
1400    {
1401      $replaceBibtex[] = chr($key);
1402      $matchBibtex[] = preg_quote("/$value/");
1403    }
1404  foreach($this->config->bibtexSpChOld as $key => $value)
1405    {
1406      $replaceBibtex[] = chr($key);
1407      $matchBibtex[] = preg_quote("/$value/");
1408    }
1409  foreach($this->config->bibtexSpChOld2 as $key => $value)
1410    {
1411      $replaceBibtex[] = chr($key);
1412      $matchBibtex[] = preg_quote("/$value/");
1413    }
1414  foreach($this->config->bibtexSpChLatex as $key => $value)
1415    {
1416      $replaceBibtex[] =  chr($key);
1417      $matchBibtex[] = preg_quote("/$value/");
1418    }
1419
1420  // Processing of the entry
1421  foreach($entry as $key => $value){
1422   // The transformation filter  has returned  latin1 code
1423   // We thus need to work with latin1.
1424   $value= $this->utf8->smartUtf8_decode($value);
1425   $entry[$key] = utf8_encode(preg_replace($matchBibtex, $replaceBibtex, $value));
1426  }
1427  return $entry;
1428}
1429
1430
1431
1432
1433}
1434
1435
1436/*****
1437 * BIBTEXCONFIG: BibTeX Configuration class
1438 *****/
1439class BIBTEXCONFIG
1440{
1441  // Constructor
1442  function BIBTEXCONFIG()
1443  {
1444  }
1445  // BibTeX arrays
1446  function bibtex()
1447  {
1448    $this->bibtexSpCh = array(
1449			      // Deal with '{' and '}' first!
1450			      0x007B	=>	"\\textbraceleft",
1451			      0x007D	=>	"\\textbraceright",
1452			      0x0022	=>	"{\"}",
1453			      0x0023	=>	"{\#}",
1454			      0x0025	=>	"{\%}",
1455			      0x0026	=>	"{\&}",
1456			      0x003C	=>	"\\textless",
1457			      0x003E	=>	"\\textgreater",
1458			      0x005F	=>	"{\_}",
1459			      0x00A3	=>	"\\textsterling",
1460			      0x00C0	=>	"{\`A}",
1461			      0x00C1	=>	"{\'A}",
1462			      0x00C2	=>	"{\^A}",
1463			      0x00C3	=>	"{\~A}",
1464			      0x00C4	=>	'{\"A}',
1465			      0x00C5	=>	"{\AA}",
1466			      0x00C6	=>	"{\AE}",
1467			      0x00C7	=>	"{\c{C}}",
1468			      0x00C8	=>	"{\`E}",
1469			      0x00C9	=>	"{\'E}",
1470			      0x00CA	=>	"{\^E}",
1471			      0x00CB	=>	'{\"E}',
1472			      0x00CC	=>	"{\`I}",
1473			      0x00CD	=>	"{\'I}",
1474			      0x00CE	=>	"{\^I}",
1475			      0x00CF	=>	'{\"I}',
1476			      0x00D1	=>	"{\~N}",
1477			      0x00D2	=>	"{\`O}",
1478			      0x00D3	=>	"{\'O}",
1479			      0x00D4	=>	"{\^O}",
1480			      0x00D5	=>	"{\~O}",
1481			      0x00D6	=>	'{\"O}',
1482			      0x00D8	=>	"{\O}",
1483			      0x00D9	=>	"{\`U}",
1484			      0x00DA	=>	"{\'U}",
1485			      0x00DB	=>	"{\^U}",
1486			      0x00DC	=>	'{\"U}',
1487			      0x00DD	=>	"{\'Y}",
1488			      0x00DF	=>	"{\ss}",
1489			      0x00E0	=>	"{\`a}",
1490			      0x00E1	=>	"{\'a}",
1491			      0x00E2	=>	"{\^a}",
1492			      0x00E3	=>	"{\~a}",
1493			      0x00E4	=>	'{\"a}',
1494			      0x00E5	=>	"{\aa}",
1495			      0x00E6	=>	"{\ae}",
1496			      0x00E7	=>	"{\c{c}}",
1497			      0x00E8	=>	"{\`e}",
1498			      0x00E9	=>	"{\'e}",
1499			      0x00EA	=>	"{\^e}",
1500			      0x00EB	=>	'{\"e}',
1501			      0x00EC	=>	"{\`\i}",
1502			      0x00ED	=>	"{\'\i}",
1503			      0x00EE	=>	"{\^\i}",
1504			      0x00EF	=>	'{\"\i}',
1505			      0x00F1	=>	"{\~n}",
1506			      0x00F2	=>	"{\`o}",
1507			      0x00F3	=>	"{\'o}",
1508			      0x00F4	=>	"{\^o}",
1509			      0x00F5	=>	"{\~o}",
1510			      0x00F6	=>	'{\"o}',
1511			      0x00F8	=>	"{\o}",
1512			      0x00F9	=>	"{\`u}",
1513			      0x00FA	=>	"{\'u}",
1514			      0x00FB	=>	"{\^u}",
1515			      0x00FC	=>	'{\"u}',
1516			      0x00FD	=>	"{\'y}",
1517			      0x00FF	=>	'{\"y}',
1518			      0x00A1	=>	"{\!}",
1519			      0x00BF	=>	"{\?}",
1520			      );
1521    //Old style with extra {} - usually array_flipped
1522    $this->bibtexSpChOld = array(
1523				 0x00C0	=>	"{\`{A}}",
1524				 0x00C1	=>	"{\'{A}}",
1525				 0x00C2	=>	"{\^{A}}",
1526				 0x00C3	=>	"{\~{A}}",
1527				 0x00C4	=>	'{\"{A}}',
1528				 0x00C5	=>	"{\A{A}}",
1529				 0x00C6	=>	"{\A{E}}",
1530				 0x00C7	=>	"{\c{C}}",
1531				 0x00C8	=>	"{\`{E}}",
1532				 0x00C9	=>	"{\'{E}}",
1533				 0x00CA	=>	"{\^{E}}",
1534				 0x00CB	=>	'{\"{E}}',
1535				 0x00CC	=>	"{\`{I}}",
1536				 0x00CD	=>	"{\'{I}}",
1537				 0x00CE	=>	"{\^{I}}",
1538				 0x00CF	=>	'{\"{I}}',
1539				 0x00D1	=>	"{\~{N}}",
1540				 0x00D2	=>	"{\`{O}}",
1541				 0x00D3	=>	"{\'{O}}",
1542				 0x00D4	=>	"{\^{O}}",
1543				 0x00D5	=>	"{\~{O}}",
1544				 0x00D6	=>	'{\"{O}}',
1545				 0x00D8	=>	"{\{O}}",
1546				 0x00D9	=>	"{\`{U}}",
1547				 0x00DA	=>	"{\'{U}}",
1548				 0x00DB	=>	"{\^{U}}",
1549				 0x00DC	=>	'{\"{U}}',
1550				 0x00DD	=>	"{\'{Y}}",
1551				 0x00DF	=>	"{\s{s}}",
1552				 0x00E0	=>	"{\`{a}}",
1553				 0x00E1	=>	"{\'{a}}",
1554				 0x00E2	=>	"{\^{a}}",
1555				 0x00E3	=>	"{\~{a}}",
1556				 0x00E4	=>	'{\"{a}}',
1557				 0x00E5	=>	"{\a{a}}",
1558				 0x00E6	=>	"{\a{e}}",
1559				 0x00E7	=>	"{\c{c}}",
1560				 0x00E8	=>	"{\`{e}}",
1561				 0x00E9	=>	"{\'{e}}",
1562				 0x00EA	=>	"{\^{e}}",
1563				 0x00EB	=>	'{\"{e}}',
1564				 0x00EC	=>	"{\`\i}",
1565				 0x00ED	=>	"{\'\i}",
1566				 0x00EE	=>	"{\^\i}",
1567				 0x00EF	=>	'{\"\i}',
1568				 0x00F1	=>	"{\~{n}}",
1569				 0x00F2	=>	"{\`{o}}",
1570				 0x00F3	=>	"{\'{o}}",
1571				 0x00F4	=>	"{\^{o}}",
1572				 0x00F5	=>	"{\~{o}}",
1573				 0x00F6	=>	'{\"{o}}',
1574				 0x00F8	=>	"{\{o}}",
1575				 0x00F9	=>	"{\`{u}}",
1576				 0x00FA	=>	"{\'{u}}",
1577				 0x00FB	=>	"{\^{u}}",
1578				 0x00FC	=>	'{\"{u}}',
1579				 0x00FD	=>	"{\'{y}}",
1580				 0x00FF	=>	'{\"{y}}',
1581				 0x00A1	=>	"{\{!}}",
1582				 0x00BF	=>	"{\{?}}",
1583				 );
1584    // And there's more?!?!?!?!? (This is not strict bibtex.....)
1585    $this->bibtexSpChOld2 = array(
1586				  0x00C0	=>	"\`{A}",
1587				  0x00C1	=>	"\'{A}",
1588				  0x00C2	=>	"\^{A}",
1589				  0x00C3	=>	"\~{A}",
1590				  0x00C4	=>	'\"{A}',
1591				  0x00C5	=>	"\A{A}",
1592				  0x00C6	=>	"\A{E}",
1593				  0x00C7	=>	"\c{C}",
1594				  0x00C8	=>	"\`{E}",
1595				  0x00C9	=>	"\'{E}",
1596				  0x00CA	=>	"\^{E}",
1597				  0x00CB	=>	'\"{E}',
1598				  0x00CC	=>	"\`{I}",
1599				  0x00CD	=>	"\'{I}",
1600				  0x00CE	=>	"\^{I}",
1601				  0x00CF	=>	'\"{I}',
1602				  0x00D1	=>	"\~{N}",
1603				  0x00D2	=>	"\`{O}",
1604				  0x00D3	=>	"\'{O}",
1605				  0x00D4	=>	"\^{O}",
1606				  0x00D5	=>	"\~{O}",
1607				  0x00D6	=>	'\"{O}',
1608				  0x00D8	=>	"\{O}",
1609				  0x00D9	=>	"\`{U}",
1610				  0x00DA	=>	"\'{U}",
1611				  0x00DB	=>	"\^{U}",
1612				  0x00DC	=>	'\"{U}',
1613				  0x00DD	=>	"\'{Y}",
1614				  0x00DF	=>	"\s{s}",
1615				  0x00E0	=>	"\`{a}",
1616				  0x00E1	=>	"\'{a}",
1617				  0x00E2	=>	"\^{a}",
1618				  0x00E3	=>	"\~{a}",
1619				  0x00E4	=>	'\"{a}',
1620				  0x00E5	=>	"\a{a}",
1621				  0x00E6	=>	"\a{e}",
1622				  0x00E7	=>	"\c{c}",
1623				  0x00E8	=>	"\`{e}",
1624				  0x00E9	=>	"\'{e}",
1625				  0x00EA	=>	"\^{e}",
1626				  0x00EB	=>	'\"{e}',
1627				  0x00EC	=>	"\`{i}",
1628				  0x00ED	=>	"\'{i}",
1629				  0x00EE	=>	"\^{i}",
1630				  0x00EF	=>	'\"{i}',
1631				  0x00F1	=>	"\~{n}",
1632				  0x00F2	=>	"\`{o}",
1633				  0x00F3	=>	"\'{o}",
1634				  0x00F4	=>	"\^{o}",
1635				  0x00F5	=>	"\~{o}",
1636				  0x00F6	=>	'\"{o}',
1637				  0x00F8	=>	"\{o}",
1638				  0x00F9	=>	"\`{u}",
1639				  0x00FA	=>	"\'{u}",
1640				  0x00FB	=>	"\^{u}",
1641				  0x00FC	=>	'\"{u}',
1642				  0x00FD	=>	"\'{y}",
1643				  0x00FF	=>	'\"{y}',
1644				  0x00A1	=>	"\{!}",
1645				  0x00BF	=>	"\{?}",
1646				  );
1647    // Latex code that some bibtex users may be using
1648    $this->bibtexSpChLatex = array(
1649				   0x00C0	=>	"\`A",
1650				   0x00C1	=>	"\'A",
1651				   0x00C2	=>	"\^A",
1652				   0x00C3	=>	"\~A",
1653				   0x00C4	=>	'\"A',
1654				   0x00C5	=>	"\AA",
1655				   0x00C6	=>	"\AE",
1656				   0x00C7	=>	"\cC",
1657				   0x00C8	=>	"\`E",
1658				   0x00C9	=>	"\'E",
1659				   0x00CA	=>	"\^E",
1660				   0x00CB	=>	'\"E',
1661				   0x00CC	=>	"\`I",
1662				   0x00CD	=>	"\'I",
1663				   0x00CE	=>	"\^I",
1664				   0x00CF	=>	'\"I',
1665				   0x00D1	=>	"\~N",
1666				   0x00D2	=>	"\`O",
1667				   0x00D3	=>	"\'O",
1668				   0x00D4	=>	"\^O",
1669				   0x00D5	=>	"\~O",
1670				   0x00D6	=>	'\"O',
1671				   0x00D8	=>	"\O",
1672				   0x00D9	=>	"\`U",
1673				   0x00DA	=>	"\'U",
1674				   0x00DB	=>	"\^U",
1675				   0x00DC	=>	'\"U',
1676				   0x00DD	=>	"\'Y",
1677				   0x00DF	=>	"\ss",
1678				   0x00E0	=>	"\`a",
1679				   0x00E1	=>	"\'a",
1680				   0x00E2	=>	"\^a",
1681				   0x00E3	=>	"\~a",
1682				   0x00E4	=>	'\"a',
1683				   0x00E5	=>	"\aa",
1684				   0x00E6	=>	"\ae",
1685				   0x00E7	=>	"\cc",
1686				   0x00E8	=>	"\`e",
1687				   0x00E9       =>	"\'e",
1688				   0x00EA	=>	"\^e",
1689				   0x00EB	=>	'\"e',
1690				   0x00EC	=>	"\`i",
1691				   0x00ED	=>	"\'i",
1692				   0x00EE	=>	"\^i",
1693				   0x00EF	=>	'\"i',
1694				   0x00F1	=>	"\~n",
1695				   0x00F2	=>	"\`o",
1696				   0x00F3	=>	"\'o",
1697				   0x00F4	=>	"\^o",
1698				   0x00F5	=>	"\~o",
1699				   0x00F6	=>	'\"o',
1700				   0x00F8	=>	"\o",
1701				   0x00F9	=>	"\`u",
1702				   0x00FA	=>	"\'u",
1703				   0x00FB	=>	"\^u",
1704				   0x00FC	=>	'\"u',
1705				   0x00FD	=>	"\'y",
1706				   0x00FF	=>	'\"y',
1707				   0x00A1	=>	"\!",
1708				   0x00BF	=>	"\?",
1709				   );
1710    $this->bibtexSpChPlain = array(
1711				   0x00C0	=>	"A",
1712				   0x00C1	=>	"A",
1713				   0x00C2	=>	"A",
1714				   0x00C3	=>	"A",
1715				   0x00C4	=>	'A',
1716				   0x00C5	=>	"A",
1717				   0x00C6	=>	"AE",
1718				   0x00C7	=>	"C",
1719				   0x00C8	=>	"E",
1720				   0x00C9	=>	"E",
1721				   0x00CA	=>	"E",
1722				   0x00CB	=>	'E',
1723				   0x00CC	=>	"I",
1724				   0x00CD	=>	"I",
1725				   0x00CE	=>	"I",
1726				   0x00CF	=>	'I',
1727				   0x00D1	=>	"N",
1728				   0x00D2	=>	"O",
1729				   0x00D3	=>	"O",
1730				   0x00D4	=>	"O",
1731				   0x00D5	=>	"O",
1732				   0x00D6	=>	'O',
1733				   0x00D8	=>	"O",
1734				   0x00D9	=>	"U",
1735				   0x00DA	=>	"U",
1736				   0x00DB	=>	"U",
1737				   0x00DC	=>	'U',
1738				   0x00DD	=>	"Y",
1739				   0x00DF	=>	"ss",
1740				   0x00E0	=>	"a",
1741				   0x00E1	=>	"a",
1742				   0x00E2	=>	"a",
1743				   0x00E3	=>	"a",
1744				   0x00E4	=>	'a',
1745				   0x00E5	=>	"aa",
1746				   0x00E6	=>	"ae",
1747				   0x00E7	=>	"c",
1748				   0x00E8	=>	"e",
1749				   0x00E9	=>	"e",
1750				   0x00EA	=>	"e",
1751				   0x00EB	=>	'e',
1752				   0x00EC	=>	"i",
1753				   0x00ED	=>	"i",
1754				   0x00EE	=>	"i",
1755				   0x00EF	=>	'i',
1756				   0x00F1	=>	"n",
1757				   0x00F2	=>	"o",
1758				   0x00F3	=>	"o",
1759				   0x00F4	=>	"o",
1760				   0x00F5	=>	"o",
1761				   0x00F6	=>	'o',
1762				   0x00F8	=>	"o",
1763				   0x00F9	=>	"u",
1764				   0x00FA	=>	"u",
1765				   0x00FB	=>	"u",
1766				   0x00FC	=>	'u',
1767				   0x00FD	=>	"u",
1768				   0x00FF	=>	'u',
1769				   0x00A1	=>	"u",
1770				   0x00BF	=>	"u",
1771				   );
1772  }
1773}
1774
1775
1776?>
1777