<?php
/************************************************** 
 *           Klasa do parsowania BBcode           * 
 ************************************************** 
 * Wersja: 1.0 RC                                 * 
 * Autor: Jacek Kowalski (http://jacekk.info)     * 
 *                                                * 
 * Utwór rozprowadzany na licencji                * 
 * http://creativecommons.org/licenses/by-nc/2.5/ * 
 **************************************************/

// Kodowanie znakow: ISO-8859-2

class bbcode {
	static $simpletags = array(
		'b' => array('<b>', '</b>'),
		'i' => array('<i>', '</i>'),
		'u' => array('<u>', '</u>'),
	);
	static $reqarg = array(
		// [tag=%]
		// 'tag' => array('<znacznik abc="$">', '</znacznik>', 'typ danych do $ = test(%, TYP);');
		'size' => array('<span style="font-size:$%;">', '</span>', 'size'),
		'color' => array('<span style="color:$;">', '</span>', 'color'),
		'bg' => array('<span style="background-color:$;">', '</span>', 'color'),
	);
	
	// Funkcja przechwytująca błędy składniowe w BBCode
	static function error($tekst) {
		echo $tekst;
	}
	
	static function test($tekst, $typ) {
		switch($typ) {
			case 'color':
				$allowed = array('aqua' => '#0FF',	'green' => '#080',	'navy' => '#008',	'silver' => '#C0C0C0',	'black' => '#000',	'grey' => '#888',	'gray' => '#888',	'olive' => '#880',	'teal' => '#088',	'blue' => '#00F',	'lime' => '#0F0',	'purple' => '#808',	'white' => '#FFF',	'fuchsia' => '#F0F',	'maroon' => '#800',	'red' => '#F00',	'yellow' => '#FF0',	'orange' => '#F80',	'pink' => '#F6B',	'violet' => '#93C',	'brown' => '#A22',	'gold' => '#FD0',	'cyan' => '#0FF',	'magenta' => '#F0F');
				if(isset($allowed[$tekst])) {
					return $allowed[$tekst];
				}
				elseif(substr($tekst, 0, 1)=='#') {
					$tekst = strtoupper(substr($tekst, 1));
					if(strlen($tekst) != 3 AND strlen($tekst) != 6) {
						self::error('Niepoprawny kolor');
						return FALSE;
					}
					else
					{
						if(trim($tekst, '0123456789ABCDEF')=='') {
							return '#'.$tekst;
						}
						else
						{
							self::error(E_WARNING, 'Niepoprawny kolor');
							return FALSE;
						}
					}
				}
				else
				{
					self::error(E_WARNING, 'Niepoprawny kolor');
					return FALSE;
				}
			break;
			case 'size':
				if(!ctype_digit($tekst)) {
					self::error(E_WARNING, 'Podana wielkość czcionki jest nieprawidłowa (dozwolone tylko cyfry)');
					return FALSE;
				}
				elseif($tekst<20) {
					self::error(E_WARNING, 'Zbyt mała wielkość czcionki (miniumum 20%)');
					return FALSE;
				}
				elseif($tekst>200) {
					self::error(E_WARNING, 'Zbyt duża wielkość czcionki (maksimum 200%)');
					return FALSE;
				}
				
				return (int)$tekst;
			break;
		}
	}
	
	static function parse($tekst) {
		$last = 0;
		$opened = array();
		$autoclosed = array();
		
		while(true) {
			$start = @strpos($tekst, '[', $last);
			if($start===FALSE) { break; }
			$end = @strpos($tekst, ']', $start);
			if($end===FALSE) { break; }
			$check = @strpos($tekst, '[', $start+1);
			if($check<$end AND $check!==FALSE) {
				$last = $check;
				continue;
			}
			
			$pre = substr($tekst, 0, $start);
			$post = substr($tekst, $end+1);
			
			$tekst = substr($tekst, $start+1, $end-$start-1);
			$tag = explode('=', strtolower($tekst));
			
			switch(count($tag)) {
				case 1:
					if(substr($tag[0], 0, 1)=='/') {
						$tag[0] = substr($tag[0], 1);
						if(($otag = array_search($tag[0], $autoclosed)) !== FALSE) {
							// Tag został już zamknięty automatycznie
							unset($autoclosed[$otag]);
							break;
						}
						elseif(!in_array($tag[0], $opened)) {
							// Tag nie był otwarty - pomiń
							$pre .= '['.$tekst.']';
							break;
						}
						else
						{
							// Zamykanie tagu i autozamykanie tagów (zła kolejność)
							while (true) {
								$otag = array_pop($opened);
								if($otag===NULL) {
									$pre .= '['.$tekst.']';
									break;
								}
								
								if(self::$simpletags[$otag]) {
									$pre .= self::$simpletags[$otag][1];
								}
								else
								{
									$pre .= self::$reqarg[$otag][1];
								}
								
								if($otag != $tag[0]) {
									// Tag autozamknięty
									array_push($autoclosed, $otag);
								}
								else
								{
									// Tag właściwy zamknięty
									break;
								}
							}
						}
					}
					elseif(self::$simpletags[$tag[0]]) {
						// Otwieranie tagu
						$pre .= self::$simpletags[$tag[0]][0];
						array_push($opened, $tag[0]);
					}
					elseif(self::$reqarg[$tag[0]])
					{
						// Tag wymaga argumentu - pomiń
						$pre .= '['.$tekst.']';
					}
					else
					{
						// Tag nieznany - pomiń
						$pre .= '['.$tekst.']';
					}
				break;
				case 2:
					if(self::$simpletags[$tag[0]]) {
						// Dany tag nie ma argumentów - pomiń
						$pre .= '['.$tekst.']';
					}
					elseif(self::$reqarg[$tag[0]])
					{
						// Otwieranie tagu
						$value = self::test($tag[1], self::$reqarg[$tag[0]][2]);
						if($value) {
							$pre .= str_replace('$', $value, self::$reqarg[$tag[0]][0]);
							array_push($opened, $tag[0]);
						}
					}
				break;
			}
			
			$tekst = $pre.$post;
			$last = strlen($pre);
		}
		
		while (true) {
			$otag = array_pop($opened);
			if($otag===NULL) {
				break;
			}
			
			if(self::$simpletags[$otag]) {
				$tekst .= self::$simpletags[$otag][1];
			}
			else
			{
				$tekst .= self::$reqarg[$otag][1];
			}
		}
		
		return $tekst;
	}
}
?>