<?php /*

 ocPortal
 Copyright (c) ocProducts, 2004-2012

 See text/EN/licence.txt for full licencing information.


 NOTE TO PROGRAMMERS:
   Do not edit this file. If you need to make changes, save your changed file to the appropriate *_custom folder
   **** If you ignore this advice, then your website upgrades (e.g. for bug fixes) will likely kill your changes ****

*/

/**
 * @license		http://opensource.org/licenses/cpal_1.0 Common Public Attribution License
 * @copyright	ocProducts Ltd
 * @package		core_rich_media
 */

/**
 * Standard code module initialisation function.
 */
function init__comcode_renderer()
{
	require_code('comcode_text');

	global $STRUCTURE_LIST;
	$STRUCTURE_LIST=array();

	global $DONT_CARE_MISSING_PAGES;
	$DONT_CARE_MISSING_PAGES=array('topics','chat','forumview','topicview','search');
}

/**
 * Check the specified URL for potentially malicious JavaScript. If any is found, the hack attack is logged, and an error is returned.
 *
 * @param  MEMBER			The member who submitted the URL
 * @param  URLPATH		The URL to check
 * @param  boolean		Whether to check as arbitrary admin
 * @return URLPATH		This is the same as the input (if something was wrong, it would never return). It's here so that the function may be used as a filter.
 */
function check_naughty_javascript_url($source_member,$url,$as_admin)
{
	global $POTENTIAL_JS_NAUGHTY_ARRAY;

	if ((!$as_admin) && (!has_specific_permission($source_member,'use_very_dangerous_comcode')))
	{
		$url2=strtolower($url);

		$matches=array();
		$bad=preg_match_all('#&\#(\d+)#',preg_replace('#\s#','',$url),$matches)!=0;
		if ($bad)
		{
			for ($i=0;$i<count($matches[0]);$i++)
			{
				$matched_entity=intval($matches[1][$i]);
				if (($matched_entity<127) && (array_key_exists(chr($matched_entity),$POTENTIAL_JS_NAUGHTY_ARRAY)))
				{
					log_hack_attack_and_exit('ASCII_ENTITY_URL_HACK',$url);
				}
			}
		}
		$bad=preg_match_all('#&\#x([\dA-Za-z][\dA-Za-z]+)#',preg_replace('#\s#','',$url),$matches)!=0;
		if ($bad)
		{
			for ($i=0;$i<count($matches[0]);$i++)
			{
				$matched_entity=intval(base_convert($matches[1][$i],16,10));
				if (($matched_entity<127) && (array_key_exists(chr($matched_entity),$POTENTIAL_JS_NAUGHTY_ARRAY)))
				{
					log_hack_attack_and_exit('ASCII_ENTITY_URL_HACK',$url);
				}
			}
		}

		$bad=(strpos($url2,'script:')!==false) || (strpos($url2,'data:')!==false);
		if ($bad) log_hack_attack_and_exit('SCRIPT_URL_HACK',$url2);
	}

	return $url; // To allow functional embedding
}

/**
 * Load up custom comcode tags so that we may parse them.
 *
 * @param  object			The database connection to use
 */
function _custom_comcode_import($connection)
{
	global $IN_MINIKERNEL_VERSION;
	global $DANGEROUS_TAGS,$VALID_COMCODE_TAGS,$BLOCK_TAGS,$TEXTUAL_TAGS,$IMPORTED_CUSTOM_COMCODE,$REPLACE_TARGETS;

	if ($IN_MINIKERNEL_VERSION==0)
	{
		// From forum driver
		if (method_exists($GLOBALS['FORUM_DRIVER'],'get_custom_bbcode'))
		{
			$custom_bbcode=$GLOBALS['FORUM_DRIVER']->get_custom_bbcode();
			foreach ($custom_bbcode as $code)
			{
				$VALID_COMCODE_TAGS[$code['tag']]=1;
				if ($code['block_tag']==1) $BLOCK_TAGS[$code['tag']]=1;
				if ($code['textual_tag']==1) $TEXTUAL_TAGS[$code['tag']]=1;
				if ($code['dangerous_tag']==1) $DANGEROUS_TAGS[$code['tag']]=1;
				//if (is_object($code['replace'])) $code['replace']=$code['replace']->evaluate();  Never tempcode
				$REPLACE_TARGETS[$code['tag']]=array('replace'=>$code['replace'],'parameters'=>$code['parameters']);
			}
		}

		// From Custom Comcode
		$tags=array();
		if (addon_installed('custom_comcode'))
		{
			if (($GLOBALS['FORUM_DB']!=$connection) && (!is_null($GLOBALS['FORUM_DB'])) && (get_forum_type()=='ocf'))
				$tags=array_merge($tags,$GLOBALS['FORUM_DB']->query_select('custom_comcode',array('tag_parameters','tag_replace','tag_tag','tag_dangerous_tag','tag_block_tag','tag_textual_tag'),array('tag_enabled'=>1)));
			if ($connection->connection_write!=$GLOBALS['SITE_DB']->connection_write)
				$tags=array_merge($tags,$GLOBALS['SITE_DB']->query_select('custom_comcode',array('tag_parameters','tag_replace','tag_tag','tag_dangerous_tag','tag_block_tag','tag_textual_tag'),array('tag_enabled'=>1)));
			$tags=array_merge($tags,$connection->query_select('custom_comcode',array('tag_parameters','tag_replace','tag_tag','tag_dangerous_tag','tag_block_tag','tag_textual_tag'),array('tag_enabled'=>1)));
		}
		foreach ($tags as $tag)
		{
			$VALID_COMCODE_TAGS[$tag['tag_tag']]=1;
			if ($tag['tag_block_tag']==1) $BLOCK_TAGS[$tag['tag_tag']]=1;
			if ($tag['tag_textual_tag']==1) $TEXTUAL_TAGS[$tag['tag_tag']]=1;
			if ($tag['tag_dangerous_tag']==1) $DANGEROUS_TAGS[$tag['tag_tag']]=1;
			//if (is_object($tag['tag_replace'])) $tag['tag_replace']=$tag['tag_replace']->evaluate();  Never tempcode
			$REPLACE_TARGETS[$tag['tag_tag']]=array('replace'=>$tag['tag_replace'],'parameters'=>$tag['tag_parameters']);
		}

		// From Comcode hooks
		$hooks=find_all_hooks('systems','comcode');
		foreach (array_keys($hooks) as $hook)
		{
			require_code('hooks/systems/comcode/'.filter_naughty_harsh($hook));
			$object=object_factory('Hook_comcode_'.filter_naughty_harsh($hook),true);

			$tag=$object->get_tag();

			$VALID_COMCODE_TAGS[$tag['tag_tag']]=1;
			if ($tag['tag_block_tag']==1) $BLOCK_TAGS[$tag['tag_tag']]=1;
			if ($tag['tag_textual_tag']==1) $TEXTUAL_TAGS[$tag['tag_tag']]=1;
			if ($tag['tag_dangerous_tag']==1) $DANGEROUS_TAGS[$tag['tag_tag']]=1;
			$REPLACE_TARGETS[$tag['tag_tag']]=array('replace'=>$tag['tag_replace'],'parameters'=>$tag['tag_parameters']);
		}
	}

	$IMPORTED_CUSTOM_COMCODE=true;
}

/**
 * Convert the specified comcode (unknown format) into a tempcode tree. You shouldn't output the tempcode tree to the browser, as it looks really horrible. If you are in a rare case where you need to output directly (not through templates), you should call the evaluate method on the tempcode object, to convert it into a string.
 *
 * @param  LONG_TEXT		The comcode to convert
 * @param  ?MEMBER		The member the evaluation is running as. This is a security issue, and you should only run as an administrator if you have considered where the comcode came from carefully (NULL: current member)
 * @param  boolean		Whether to explicitly execute this with admin rights. There are a few rare situations where this should be done, for data you know didn't come from a member, but is being evaluated by one.
 * @param  ?integer		The position to conduct wordwrapping at (NULL: do not conduct word-wrapping)
 * @param  ?string		A special identifier that can identify this resource in a sea of our resources of this class; usually this can be ignored, but may be used to provide a binding between Javascript in evaluated comcode, and the surrounding environment (NULL: no explicit binding)
 * @param  ?object		The database connection to use (NULL: standard site connection)
 * @param  boolean		Whether to parse so as to create something that would fit inside a semihtml tag. It means we generate HTML, with Comcode written into it where the tag could never be reverse-converted (e.g. a block).
 * @param  boolean		Whether this is being pre-parsed, to pick up errors before row insertion.
 * @param  boolean		Whether to treat this whole thing as being wrapped in semihtml, but apply normal security otherwise.
 * @param  boolean		Whether we are only doing this parse to find the title structure
 * @param  boolean		Whether to only check the Comcode. It's best to use the check_comcode function which will in turn use this parameter.
 * @param  ?array			A list of words to highlight (NULL: none)
 * @param  ?MEMBER		The member we are running on behalf of, with respect to how attachments are handled; we may use this members attachments that are already within this post, and our new attachments will be handed to this member (NULL: member evaluating)
 * @return tempcode		The tempcode generated
 */
function _comcode_to_tempcode($comcode,$source_member=NULL,$as_admin=false,$wrap_pos=60,$pass_id=NULL,$connection=NULL,$semiparse_mode=false,$preparse_mode=false,$is_all_semihtml=false,$structure_sweep=false,$check_only=false,$highlight_bits=NULL,$on_behalf_of_member=NULL)
{
	/*if (get_option('anti_leech',true)==='1')		No, better to see it, even if custom settings are lost
	{
		unset($REVERSABLE_TAGS['attachment_safe']);
		unset($REVERSABLE_TAGS['attachment2']);
	}*/

	if (is_null($connection)) $connection=$GLOBALS['SITE_DB'];

	if (is_null($source_member))
	{
		$source_member=(function_exists('get_member'))?get_member():0;
	}

	if (!$structure_sweep) $comcode=unixify_line_format($comcode); // Done already if this is a structure sweep

	// Ensures the 'title' tags are incremental with their anchors
	global $STRUCTURE_LIST;
	$old_structure_list=$STRUCTURE_LIST;
	$STRUCTURE_LIST=array();

	if (substr($comcode,0,8)=='<comcode')
	{
		require_code('comcode_xml');

		if (!$as_admin) check_specific_permission('comcode_dangerous',NULL,$source_member);
		$parser=new comcode_xml_to_tempcode($comcode,$source_member,$wrap_pos,$pass_id,$connection,$semiparse_mode,$preparse_mode,$is_all_semihtml,$structure_sweep,$check_only,$on_behalf_of_member);
		$ret=$parser->tempcode;
	} else
	{
		require_code('comcode_text');

		$ret=comcode_text_to_tempcode($comcode,$source_member,$as_admin,$wrap_pos,$pass_id,$connection,$semiparse_mode,$preparse_mode,$is_all_semihtml,$structure_sweep,$check_only,$highlight_bits,$on_behalf_of_member);
	}

	$STRUCTURE_LIST=$old_structure_list; // Restore, so that Comcode pages being loaded up in search results don't get skewed TOC's

	return $ret;
}

/**
 * Show a comcode parser error.
 *
 * @param  boolean		Whether this is being pre-parsed, to pick up errors before row insertion.
 * @param  array			Error message details to pass to do_lang, or if the first in the list is NULL, use directly
 * @param  integer		The position during parsing that the error occurred at
 * @param  LONG_TEXT		The comcode the parser error occurred in
 * @param  boolean		Whether to only check the Comcode.
 * @return tempcode		An error message to put in the output stream (shown in certain situations, where in other situations we bomb out).
 */
function comcode_parse_error($preparse_mode,$_message,$pos,$comcode,$check_only=false)
{
	//echo $comcode;

	require_lang('comcode');
	if (is_null($_message[0]))
	{
		$message=$_message[1];
	} else
	{
		if (strpos($_message[0],':')===false) $_message[0]='comcode:'.$_message[0];
		$message=call_user_func_array('do_lang_tempcode',array_map('escape_html',$_message));
	}

	$posted=false;
	foreach ($_POST+$_GET as $name=>$val)
	{
		if (is_array($val)) continue;

		if ((post_param($name,'')==$comcode) || (substr($name,-7)=='_parsed')) $posted=true;
	}
	if (!$check_only)
	{
		if (((get_page_name()=='admin_import') || (count($_POST)==0) || /*(strpos($comcode,']new_')!==false) || */(!$posted)) && (!$preparse_mode))
		{
			$line=substr_count(substr($comcode,0,$pos),chr(10))+1;
			$out=do_template('COMCODE_CRITICAL_PARSE_ERROR',array('LINE'=>integer_format($line),'MESSAGE'=>$message,'SOURCE'=>$comcode)); // Won't parse, but we can't help it, so we will skip on
			return $out;
		}
	}

	$len=strlen($comcode);
	$lines=new ocp_tempcode();
	$number=1;
	$sofar='';
	$line=NULL;
	for ($i=0;$i<$len;$i++)
	{
		$char=$comcode[$i];
		if ($i==$pos)
		{
			$tmp_tpl=do_template('COMCODE_MISTAKE_ERROR');
			$sofar.=$tmp_tpl->evaluate();
			$line=$number;
		}
		if ($char==chr(10))
		{
			$lines->attach(do_template('COMCODE_MISTAKE_LINE',array('_GUID'=>'2022be3de10590d525f333b6ac0da37b','NUMBER'=>integer_format($number),'LINE'=>make_string_tempcode($sofar))));
			$sofar='';
			$number++;
		} $sofar.=escape_html($char);
	}
	if ($i==$pos)
	{
		$tmp_tpl=do_template('COMCODE_MISTAKE_ERROR');
		$sofar.=$tmp_tpl->evaluate();
	}
	$lines->attach(do_template('COMCODE_MISTAKE_LINE',array('_GUID'=>'eebfe1342f3129d4e31fc9fc1963af2b','NUMBER'=>integer_format($number),'LINE'=>make_string_tempcode($sofar))));
	if (is_null($line)) $line=$number;

	// Now, using some kind of miracle, we need to find out what parameter name blew-up. Let's look through the parameters and see what
	// is equal to $comcode. I'd rather not do this in a hackerish way - but the architecture was not designed for this.
	$name=NULL;
	foreach ($_POST as $key=>$val)
	{
		if (!is_string($val)) continue;
		if (post_param($key)==$comcode) // We have to use post_param, because it might be unix-converted / word-filtered
		{
			$name=$key;
			break;
		}
	}
	if (is_null($name))
	{
		if ($check_only) // Maybe it has been appended with something else, so search deeper (we suspect this as we have been explictly asked to check the Comcode)
		{
			foreach ($_POST as $key=>$val)
			{
				if (!is_string($val)) continue;
				$val=post_param($key);
				if ((strlen($val)>10) && ((strpos($comcode,$val)===0) || (strpos($comcode,$val)==strlen($comcode)-strlen($val))))
				{
					$name=$key;
					break;
				}
			}
		}
		if (is_null($name))
			warn_exit(do_lang_tempcode('COMCODE_ERROR',$message,integer_format($line)));
	}

	if (!running_script('comcode_convert')) // Don't want it running in background
	{
		$GLOBALS['HTTP_STATUS_CODE']='400';
		if (!headers_sent())
		{
			if (/*(!browser_matches('ie')) && */(strpos(ocp_srv('SERVER_SOFTWARE'),'IIS')===false)) header('HTTP/1.0 400 Bad Request');
		}
	}

	// Output our error / correction form
	@ob_end_clean();
	$hidden=build_keep_post_fields(array($name));
	require_code('form_templates');
	$fields=form_input_text_comcode(do_lang_tempcode('NEW'),do_lang_tempcode('COMCODE_REPLACEMENT'),$name,$comcode,true,NULL,true);
	$post_url=get_self_url();
	$form=do_template('FORM',array('_GUID'=>'207bad1252add775029b34ba36e02856','URL'=>$post_url,'TEXT'=>'','HIDDEN'=>$hidden,'FIELDS'=>$fields,'SUBMIT_NAME'=>do_lang_tempcode('PROCEED')));
	$output=do_template('COMCODE_MISTAKE_SCREEN',array('_GUID'=>'0010230e6612b0775566d07ddf54305a','EDITABLE'=>!running_script('preview'),'FORM'=>$form,'TITLE'=>get_page_title('ERROR_OCCURRED'),'LINE'=>integer_format($line),'MESSAGE'=>$message,'LINES'=>$lines));
	$echo=new ocp_tempcode();
	if (!running_script('preview'))
	{
		$echo=globalise($output,NULL,'',true);
		$echo->handle_symbol_preprocessing();
	} else
	{
		$echo->attach(do_template('STYLED_HTML_WRAP',array('TITLE'=>do_lang_tempcode('PREVIEW'),'TARGET'=>'_top','CONTENT'=>$output)));
	}
	$echo->evaluate_echo();
	exit();
	return new ocp_tempcode(); // to trick code checker
}

/**
 * Build some Comcode-syntax attribute parameters from our in-memory parameters.
 *
 * @param  array			In-memory array.
 * @return string			Reconstructed syntax.
*/
function reinsert_parameters($attributes)
{
	$out='';
	foreach ($attributes as $key=>$val)
	{
		$out.=' '.$key.'="'.comcode_escape($val).'"';
	}
	return $out;
}

/**
 * Test a URL as a broken link.
 *
 * @param  URLPATH		URL to test.
 * @param  string			Comcode tag type, to which the URL is associated.
 * @param  string			URL actually provided.
 * @param  MEMBER			The member who is responsible for this Comcode
 * @return tempcode		Error message, or blank if no error.
*/
function test_url($url_full,$tag_type,$given_url,$source_member)
{
	if (get_option('check_broken_urls')=='0') return new ocp_tempcode();
	if (strpos($url_full,'{$')!==false) return new ocp_tempcode();

	$temp_tpl=new ocp_tempcode();
	if (!handle_has_checked_recently($url_full))
	{
		$GLOBALS['COMCODE_PARSE_URLS_CHECKED']++;
		$test=($GLOBALS['COMCODE_PARSE_URLS_CHECKED']>=MAX_URLS_TO_READ)?'':http_download_file($url_full,0,false);
		if ((is_null($test)) && (in_array($GLOBALS['HTTP_MESSAGE'],array('404','could not connect to host'))))
		{
			$temp_tpl=do_template('WARNING_TABLE',array('FOR_GUESTS'=>false,'WARNING'=>do_lang_tempcode('MISSING_URL_COMCODE',$tag_type,escape_html($url_full))));
			if (array_key_exists('COMCODE_BROKEN_URLS',$GLOBALS))
			{
				$GLOBALS['COMCODE_BROKEN_URLS'][]=array($url_full,NULL);
			} elseif ((!in_array(get_page_name(),$GLOBALS['DONT_CARE_MISSING_PAGES'])) && (!running_script('iframe')) && (!running_script('comcode_convert')) && (!running_script('preview')))
			{
				$found_in_post=false; // We don't want to send email if someone's just posting it right now, because they'll see the error on their screen, and we don't want staff spammed by member mistakes
				foreach ($_POST as $val)
				{
					if (is_array($_POST)) continue;
					if (get_magic_quotes_gpc()) $val=stripslashes($val);
					if ((is_string($val)) && (strpos($val,$given_url)!==false)) $found_in_post=true;
				}
				if (!$found_in_post)
				{
					require_code('failure');
					relay_error_notification(do_lang('MISSING_URL_COMCODE',$tag_type,$url_full),false,$GLOBALS['FORUM_DRIVER']->is_staff($source_member)?'error_occurred_missing_reference_important':'error_occurred_missing_reference');
				}
			}
		}
	}
	return $temp_tpl;
}

/**
 * Render a code box.
 *
 * @param  string			The data type (e.g. file extension) we are rendering.
 * @param  tempcode		Contents (code) to render.
 * @param  boolean		Whether to show line numbers.
 * @param  boolean		Whether what we have came from inside a semihtml tag
 * @param  boolean		Whether what we have came from semihtml mode
 * @return array			A pair: The tempcode for the code box, and the title of the box
 */
function do_code_box($type,$embed,$numbers=true,$in_semihtml=false,$is_all_semihtml=false)
{
	$_embed=mixed();
	$title=mixed();
	if ((file_exists(get_file_base().'/sources/geshi/'.filter_naughty(strtolower($type)).'.php')) || (file_exists(get_file_base().'/sources_custom/geshi/'.filter_naughty($type).'.php')))
	{
		$evaluated=$embed->evaluate();

		if (($in_semihtml) || ($is_all_semihtml))
		{
			require_code('comcode_from_html');
			$evaluated=semihtml_to_comcode($evaluated);
		}

		require_code('geshi');
		if (class_exists('GeSHi'))
		{
			require_code('developer_tools');
			destrictify();
			$geshi=new GeSHi($evaluated,strtolower($type));
			$geshi->set_header_type(GESHI_HEADER_DIV);
			if ($numbers) $geshi->enable_line_numbers(GESHI_NORMAL_LINE_NUMBERS);
			require_lang('comcode');
			$title=do_lang_tempcode('comcode:CODE_IN_LANGUAGE',escape_html($type));
			require_code('xhtml');
			$_embed=xhtmlise_html($geshi->parse_code());
			restrictify();
		}
	} else
	{
		switch (strtolower($type))
		{
			case 'php':
				if (!function_exists('highlight_string')) break;

				$evaluated=$embed->evaluate();

				if (($in_semihtml) || ($is_all_semihtml))
				{
					require_code('comcode_from_html');
					$evaluated=semihtml_to_comcode($evaluated);
				}

				if (strpos($evaluated,'<'.'?php')===false)
				{
					$strip=true;
					$evaluated="<"."?php\n".$evaluated."\n?".">";
				} else $strip=false;
				require_code('xhtml');
				if (defined('HIPHOP_PHP'))
				{
					$h_result=nl2br(escape_html($evaluated));
				} else
				{
					ob_start();
					highlight_string($evaluated);
					$h_result=ob_get_contents();
					ob_end_clean();
				}
				$_embed=xhtmlise_html($h_result);
				if ($strip)
				{
					$_embed=str_replace('&lt;?php<br />','',$_embed);
					$_embed=str_replace('?&gt;','',$_embed);
				}
				$title=do_lang_tempcode('PHP_CODE');
				break;
		}
	}

	return array($_embed,$title);
}

/**
 * Get tempcode for a Comcode tag. This function should always return (errors should be placed in the Comcode output stream), for stability reasons (i.e. if you're submitting something, you can't have the whole submit process die half way through in an unstructured fashion).
 *
 * @param  string			The tag being converted
 * @param  array			A map of the attributes (name=>val) for the tag. Val is usually a string, although in select places, the XML parser may pass tempcode.
 * @param  mixed			Tempcode of the inside of the tag ([between]THIS[/between]); the XML parser may pass in special stuff here, which is interpreted only for select tags
 * @param  boolean		Whether we are allowed to proceed even if this tag is marked as 'dangerous'
 * @param  string			A special identifier to mark where the resultant tempcode is going to end up (e.g. the ID of a post)
 * @param  integer		The position this tag occurred at in the Comcode
 * @param  MEMBER			The member who is responsible for this Comcode
 * @param  boolean		Whether to check as arbitrary admin
 * @param  object			The database connection to use
 * @param  string			The whole chunk of comcode
 * @param  boolean		Whether this is for WML output
 * @param  boolean		Whether this is only a structure sweep
 * @param  boolean		Whether we are in semi-parse-mode (some tags might convert differently)
 * @param  ?array			A list of words to highlight (NULL: none)
 * @param  ?MEMBER		The member we are running on behalf of, with respect to how attachments are handled; we may use this members attachments that are already within this post, and our new attachments will be handed to this member (NULL: member evaluating)
 * @param  boolean		Whether what we have came from inside a semihtml tag
 * @param  boolean		Whether what we have came from semihtml mode
 * @return tempcode		The tempcode for the Comcode
 */
function _do_tags_comcode($tag,$attributes,$embed,$comcode_dangerous,$pass_id,$marker,$source_member,$as_admin,$connection,&$comcode,$wml,$structure_sweep,$semiparse_mode,$highlight_bits=NULL,$on_behalf_of_member=NULL,$in_semihtml=false,$is_all_semihtml=false)
{
	if (($structure_sweep) && ($tag!='title')) return new ocp_tempcode();

	$param_given=isset($attributes['param']);
	if ((!isset($attributes['param'])) && ($tag!='block')) $attributes['param']='';

	global $DANGEROUS_TAGS,$STRUCTURE_LIST,$COMCODE_PARSE_TITLE;
	if ((isset($DANGEROUS_TAGS[$tag])) && (!$comcode_dangerous))
	{
		//if (($source_member==get_member()) && (strpos(serialize($_POST),'['.$tag)!==false))
		{
			$username=$GLOBALS['FORUM_DRIVER']->get_username($source_member);
			if ($semiparse_mode) // Can't load through error for this, so just show it as a tag
			{
				$params='';
				foreach ($attributes as $key=>$val)
				{
					$params.=' '.$key.'="'.comcode_escape($val).'"';
				}
				return make_string_tempcode('<input class="ocp_keep_ui_controlled" size="45" title="['.$tag.''.(escape_html($params)).']'.((($in_semihtml) || ($is_all_semihtml))?$embed->evaluate():(escape_html($embed->evaluate()))).'[/'.$tag.']" type="text" value="'.($tag=='block'?do_lang('COMCODE_EDITABLE_BLOCK',escape_html($embed->evaluate())):do_lang('COMCODE_EDITABLE_TAG',escape_html($tag))).'" />');
			}
			return do_template('WARNING_TABLE',array('WARNING'=>do_lang_tempcode('comcode:NO_ACCESS_FOR_TAG',escape_html($tag),escape_html($username))));
		}
		//return new ocp_tempcode();
	}

	// These are just bbcode compatibility tags.. we will remap to our proper comcode
	if ($tag=='php')
	{
		$attributes['param']='php';
		$tag='code';
	}
	elseif ($tag=='sql')
	{
		$attributes['param']='sql';
		$tag='code';
	}
	elseif ($tag=='codebox')
	{
		$attributes['scroll']=1;
		$tag='code';
	}
	elseif ($tag=='left')
	{
		$attributes['param']='left';
		$tag='align';
	}
	elseif ($tag=='center')
	{
		$attributes['param']='center';
		$tag='align';
	}
	elseif ($tag=='right')
	{
		$attributes['param']='right';
		$tag='align';
	}
	elseif ($tag=='thread')
	{
		$tag='topic';
	}
	elseif (($tag=='internal_table') || ($tag=='external_table'))
	{
		$tag='box';
		if (array_key_exists('class',$attributes)) $attributes['type']=$attributes['class'];
	}

	if ($semiparse_mode) // We have got to this point because we want to provide a special 'button' editing representation for these tags
	{
		$non_text_tags=array('attachment','section_controller','big_tab_controller','currency','block','contents','concepts','flash','menu','email','reference','upload','page','exp_thumb','exp_ref','thumb','snapback','post','thread','topic','include','random','jumping','shocker'); // Also in JAVASCRIPT_EDITING.tpl
		if ($tag=='attachment_safe')
		{
			if (preg_match('#^new\_\d+$#',$embed->evaluate())!=0)
				$non_text_tags[]='attachment_safe';
		}
		if (in_array($tag,$non_text_tags))
		{
			$params='';
			foreach ($attributes as $key=>$val)
			{
				$params.=' '.$key.'="'.comcode_escape($val).'"';
			}
			return make_string_tempcode('<input class="ocp_keep_ui_controlled" size="45" title="['.$tag.''.(escape_html($params)).']'.((($in_semihtml) || ($is_all_semihtml))?$embed->evaluate():(escape_html($embed->evaluate()))).'[/'.$tag.']" type="text" value="'.($tag=='block'?do_lang('COMCODE_EDITABLE_BLOCK',escape_html($embed->evaluate())):do_lang('COMCODE_EDITABLE_TAG',escape_html($tag))).'" />');
		}
	}

	$temp_tpl=new ocp_tempcode();
	switch ($tag)
	{
		case 'no_parse':
			$temp_tpl->attach($embed);
			break;
		case 'currency':
			if (addon_installed('ecommerce'))
			{
				$bracket=(array_key_exists('bracket',$attributes) && ($attributes['bracket']=='1'));
				if ($attributes['param']=='') $attributes['param']=get_option('currency');
				$temp_tpl=do_template('COMCODE_CURRENCY',array('_GUID'=>'ee1fcdae082af6397ff3bad89006e012','AMOUNT'=>$embed,'FROM_CURRENCY'=>$attributes['param'],'BRACKET'=>$bracket));
			}
			break;
		case 'overlay':
			$x=strval(array_key_exists('x',$attributes)?intval($attributes['x']):100);
			$y=strval(array_key_exists('y',$attributes)?intval($attributes['y']):100);
			$width=strval(array_key_exists('width',$attributes)?intval($attributes['width']):300);
			$height=strval(array_key_exists('height',$attributes)?intval($attributes['height']):300);
			$timein=strval(array_key_exists('timein',$attributes)?intval($attributes['timein']):0);
			$timeout=strval(array_key_exists('timeout',$attributes)?intval($attributes['timeout']):-1);
			$temp_tpl=do_template('COMCODE_OVERLAY',array('_GUID'=>'dfd0f7a72cc2bf6b613b28f8165a0034','UNIQ_ID'=>'a'.uniqid(''),'EMBED'=>$embed,'ID'=>($attributes['param']!='')?$attributes['param']:('rand'.uniqid('')),'X'=>$x,'Y'=>$y,'WIDTH'=>$width,'HEIGHT'=>$height,'TIMEIN'=>$timein,'TIMEOUT'=>$timeout));
			break;
		case 'code':
			if ($wml)
			{
				$temp_tpl->attach('<b>');
				$temp_tpl->attach($embed);
				$temp_tpl->attach('</b>');
				break;
			}

			list($_embed,$title)=do_code_box($attributes['param'],$embed,(array_key_exists('numbers',$attributes)) && ($attributes['numbers']==1),$in_semihtml,$is_all_semihtml);
			if (!is_null($_embed))
			{
				$tpl=(array_key_exists('scroll',$attributes) && ($attributes['scroll']==1))?'COMCODE_CODE_SCROLL':'COMCODE_CODE';
				if (($tpl=='COMCODE_CODE_SCROLL') && (substr_count($_embed,chr(10))<10))
				{
					$style='height: auto';
				} else $style='';
				$temp_tpl=do_template($tpl,array('_GUID'=>'c5d46d0927272fcacbbabcfab0ef6b0c','STYLE'=>$style,'TYPE'=>$attributes['param'],'CONTENT'=>$_embed,'TITLE'=>$title));
			} else $_embed='';
			if ($temp_tpl->is_empty())
			{
				if (($in_semihtml) || ($is_all_semihtml)) // Yuck. We've double converted. Ideally we would have parsed a direct stream of HTML. But we could not do that for security reasons.
				{
					require_code('comcode_from_html');
					$back_to_comcode=semihtml_to_comcode($embed->evaluate()); // Undo what's happened already
					//$back_to_comcode=html_entity_decode($back_to_comcode,ENT_QUOTES,get_charset()); // Remove the escaping entities that were inside the code tag
					$embed=comcode_to_tempcode($back_to_comcode,$source_member,$as_admin,80,$pass_id,$connection,true); // Re-parse (with full security)
				}

				$_embed=$embed->evaluate();

				if ((!array_key_exists('scroll',$attributes)) && (strlen($_embed)>1000)) $attributes['scroll']=1;
				$tpl=(array_key_exists('scroll',$attributes) && ($attributes['scroll']==1))?'COMCODE_CODE_SCROLL':'COMCODE_CODE';
				$title=do_lang_tempcode('CODE');
				if (($tpl=='COMCODE_CODE_SCROLL') && (substr_count($_embed,chr(10))<10))
				{
					$style='height: auto';
				} else $style='';
				$temp_tpl=do_template($tpl,array('CONTENT'=>$_embed,'TITLE'=>$title,'STYLE'=>$style,'TYPE'=>$attributes['param']));
			}
			break;
		case 'list':
			if (is_array($embed))
			{
				$parts=$embed;
			} else
			{
				$_embed=trim($embed->evaluate());
				$_embed=str_replace('[/*]','',$_embed);
				$parts=explode('[*]',$_embed);
			}

			if (isset($temp_tpl->preprocessable_bits))
				$temp_tpl->preprocessable_bits=array_merge($temp_tpl->preprocessable_bits,$embed->preprocessable_bits);

			if ($wml)
			{
				foreach ($parts as $i=>$part)
				{
					if (($i==0) && (str_replace(array('&nbsp;','<br />',' '),array('','',''),trim($part))=='')) continue;
					$temp_tpl->attach('<br />* ');
					$temp_tpl->attach($part);
				}
				$temp_tpl->attach('<br />* ');
				break;
			}

			$type=$attributes['param'];

			if ($type!='')
			{
				if ($type=='1') $type='decimal';
				elseif ($type=='a') $type='lower-alpha';
				elseif ($type=='i') $type='lower-roman';
				elseif ($type=='x') $type='none';
				elseif (!in_array($type,array('circle','disc','square','armenian','decimal','decimal-leading-zero','georgian','lower-alpha','lower-greek','lower-latin','lower-roman','upper-alpha','upper-latin','upper-roman'))) $type='disc';
				$tag=in_array($type,array('circle','disc','square'))?'ul':'ol';
				$temp_tpl->attach('<'.$tag.' style="list-style-type: '.$type.'">');
				foreach ($parts as $i=>$part)
				{
					if (($i==0) && (str_replace(array('&nbsp;','<br />',' '),array('','',''),trim($part))=='')) continue;
					$temp_tpl->attach('<li>'.preg_replace('#\<br /\>(\&nbsp;|\s)*$#D','',preg_replace('#^\<br /\>(\&nbsp;|\s)*#D','',$part)).'</li>');
				}
				$temp_tpl->attach('</'.$tag.'>');
			} else
			{
				$temp_tpl->attach('<ul>');
				foreach ($parts as $i=>$part)
				{
					if (($i==0) && (str_replace(array('&nbsp;','<br />',' '),array('','',''),trim($part))=='')) continue;
					$temp_tpl->attach('<li>'.preg_replace('#\<br /\>(\&nbsp;|\s)*$#D','',preg_replace('#^\<br /\>(\&nbsp;|\s)*#D','',$part)).'</li>');
				}
				$temp_tpl->attach('</ul>');
			}
			break;
		case 'snapback':
			require_lang('ocf');
			$post_id=intval($embed->evaluate());
			$s_title=($attributes['param']=='')?do_lang_tempcode('FORUM_POST_NUMBERED',integer_format($post_id)):make_string_tempcode($attributes['param']);
			$forum=array_key_exists('forum',$attributes)?$attributes['forum']:'';
			$temp_tpl=do_template('COMCODE_SNAPBACK',array('URL'=>$GLOBALS['FORUM_DRIVER']->post_url($post_id,$forum),'TITLE'=>$s_title));
			break;
		case 'post':
			require_lang('ocf');
			$post_id=intval($embed->evaluate());
			$s_title=($attributes['param']=='')?do_lang_tempcode('FORUM_POST_NUMBERED',integer_format($post_id)):make_string_tempcode($attributes['param']);
			$forum=array_key_exists('forum',$attributes)?$attributes['forum']:'';
			$temp_tpl->attach(hyperlink($GLOBALS['FORUM_DRIVER']->post_url($post_id,$forum),$s_title));
			break;
		case 'topic':
			require_lang('ocf');
			$topic_id=intval($embed->evaluate());
			$s_title=($attributes['param']=='')?do_lang_tempcode('FORUM_TOPIC_NUMBERED',integer_format($topic_id)):make_string_tempcode($attributes['param']);
			$forum=array_key_exists('forum',$attributes)?$attributes['forum']:'';
			$temp_tpl->attach(hyperlink($GLOBALS['FORUM_DRIVER']->topic_url($topic_id,$forum),$s_title));
			break;
		case 'staff_note':
			$temp_tpl=new ocp_tempcode();
			return $temp_tpl;
		case 'section':
			if ($wml)
			{
				$temp_tpl=$embed;
				break;
			}

			$name=(array_key_exists('param',$attributes))?$attributes['param']:'section'.strval(mt_rand(0,100));
			$default=(array_key_exists('default',$attributes))?$attributes['default']:'0';
			$temp_tpl=do_template('COMCODE_SECTION',array('_GUID'=>'a902962ccdc80046c999d6fed907d105','PASS_ID'=>'x'.$pass_id,'DEFAULT'=>$default=='1','NAME'=>$name,'CONTENT'=>$embed));
			break;
		case 'section_controller':
			if ($wml)
			{
				break;
			}

			$sections=explode(',',$embed->evaluate());
			$temp_tpl=do_template('COMCODE_SECTION_CONTROLLER',array('_GUID'=>'133bf24892e9e3ec2a01146d6ec418fe','SECTIONS'=>$sections,'PASS_ID'=>'x'.$pass_id));
			break;
		case 'big_tab':
			if ($wml)
			{
				$temp_tpl=$embed;
				break;
			}

			$name=(array_key_exists('param',$attributes))?$attributes['param']:'big_tab'.strval(mt_rand(0,100));
			$default=(array_key_exists('default',$attributes))?$attributes['default']:'0';
			$temp_tpl=do_template('COMCODE_BIG_TABS_TAB',array('PASS_ID'=>'x'.$pass_id,'DEFAULT'=>$default=='1','NAME'=>$name,'CONTENT'=>$embed));
			break;
		case 'big_tab_controller':
			if ($wml)
			{
				break;
			}

			$tabs=explode(',',$embed->evaluate());
			if (!array_key_exists('switch_time',$attributes)) $attributes['switch_time']='6000';
			$temp_tpl=do_template('COMCODE_BIG_TABS_CONTROLLER',array('SWITCH_TIME'=>$attributes['switch_time'],'TABS'=>$tabs,'PASS_ID'=>'x'.$pass_id));
			break;
		case 'tab':
			if ($wml)
			{
				$temp_tpl=$embed;
				break;
			}

			$default=(array_key_exists('default',$attributes))?$attributes['default']:'0';
			$temp_tpl=do_template('COMCODE_TAB_BODY',array('DEFAULT'=>$default=='1','TITLE'=>trim($attributes['param']),'CONTENT'=>$embed));
			break;
		case 'tabs':
			if ($wml)
			{
				break;
			}

			$heads=new ocp_tempcode();
			$tabs=explode(',',$attributes['param']);
			foreach ($tabs as $i=>$tab)
			{
				$heads->attach(do_template('COMCODE_TAB_HEAD',array('TITLE'=>trim($tab),'FIRST'=>$i==0,'LAST'=>!array_key_exists($i+1,$tabs))));
			}

			$temp_tpl=do_template('COMCODE_TAB_CONTROLLER',array('HEADS'=>$heads,'CONTENT'=>$embed));
			break;
		case 'carousel':
			if ($attributes['param']=='') $attributes['param']='40';
			$temp_tpl=do_template('COMCODE_CAROUSEL',array('CONTENT'=>$embed,'SCROLL_AMOUNT'=>$attributes['param']));
			break;
		case 'menu':
			if ($wml)
			{
				break;
			}

			$name=(array_key_exists('param',$attributes))?$attributes['param']:'mnu'.strval(mt_rand(0,100));
			$type=(array_key_exists('type',$attributes))?$attributes['type']:'tree';
			require_code('menus');
			require_code('menus_comcode');
			$temp_tpl=build_comcode_menu($embed->evaluate(),$name,$source_member,$type);
			break;
		case 'if_in_group':
			$groups='';
			$_groups=explode(',',$attributes['param']);
			$all_groups=$GLOBALS['FORUM_DRIVER']->get_usergroup_list();
			foreach ($_groups as $group)
			{
				$find=array_search($group,$all_groups);
				if ($find===false)
				{
					if ($groups!='') $groups.=',';
					$groups.=$group;
				} else
				{
					if ($groups!='') $groups.=',';
					$groups.=strval($find);
				}
			}
			$temp_tpl=do_template('COMCODE_IF_IN_GROUP',array('_GUID'=>'761a7cc07f7b4b68508d68ce19b87d2c','TYPE'=>array_key_exists('type',$attributes)?$attributes['type']:'','CONTENT'=>$embed,'GROUPS'=>$groups));
			break;
		case 'acronym':
		case 'abbr':
			$temp_tpl=do_template('COMCODE_ABBR',array('_GUID'=>'acbc4f991dsf03f81b61919b74ac24c91','CONTENT'=>$embed,'TITLE'=>$attributes['param']));
			break;
		case 'address':
			$temp_tpl=do_template('COMCODE_ADDRESS',array('_GUID'=>'acbcsdf9910703f81b61919b74ac24c91','CONTENT'=>$embed));
			break;
		case 'dfn':
			$temp_tpl=do_template('COMCODE_DFN',array('_GUID'=>'acbc4f9910703f81b61sf19b74ac24c91','CONTENT'=>$embed));
			break;
		case 'pulse':
			$min_color=array_key_exists('min',$attributes)?$attributes['min']:'0000FF';
			$max_color=array_key_exists('max',$attributes)?$attributes['max']:'FF0044';
			if (substr($min_color,0,1)=='#') $min_color=substr($min_color,1);
			if (substr($max_color,0,1)=='#') $max_color=substr($max_color,1);
			$speed=($attributes['param']=='')?100:intval($attributes['param']);

			$temp_tpl=do_template('COMCODE_PULSE',array('_GUID'=>'adsd4f9910sfd03f81b61919b74ac24c91','RAND_ID'=>uniqid(''),'CONTENT'=>$embed,'MIN_COLOR'=>$min_color,'MAX_COLOR'=>$max_color,'SPEED'=>strval($speed)));
			break;
		case 'del':
			$cite=array_key_exists('cite',$attributes)?$attributes['cite']:NULL;
			if (!is_null($cite))
			{
				$temp_tpl=test_url($cite,'del',$cite,$source_member);
			}
			$datetime=array_key_exists('datetime',$attributes)?$attributes['datetime']:NULL;
			$temp_tpl->attach(do_template('COMCODE_DEL',array('_GUID'=>'acsd4f9910sfd03f81b61919b74ac24c91','CONTENT'=>$embed,'CITE'=>$cite,'DATETIME'=>$datetime)));
			break;
		case 'ins':
			$cite=array_key_exists('cite',$attributes)?$attributes['cite']:NULL;
			if (!is_null($cite))
			{
				$temp_tpl=test_url($cite,'ins',$cite,$source_member);
				if (!$temp_tpl->is_empty()) break;
			}
			$datetime=array_key_exists('datetime',$attributes)?$attributes['datetime']:NULL;
			$temp_tpl->attach(do_template('COMCODE_INS',array('_GUID'=>'asss4f9910703f81b61919bsfc24c91','CONTENT'=>$embed,'CITE'=>$cite,'DATETIME'=>$datetime)));
			break;
		case 'cite':
			$temp_tpl=do_template('COMCODE_CITE',array('_GUID'=>'acbcsf910703f81b61919b74ac24c91','CONTENT'=>$embed));
			break;
		case 'b':
			if ($semiparse_mode)
			{
				$temp_tpl=make_string_tempcode('<b>'.$embed->evaluate().'</b>');
				break;
			}
			$temp_tpl=do_template('COMCODE_BOLD',array('_GUID'=>'acbc4fds910703f81b619sf74ac24c91','CONTENT'=>$embed));
			break;
		case 'align':
			if ($wml)
			{
				$temp_tpl=$embed;
				break;
			}

			$align=array_key_exists('param',$attributes)?$attributes['param']:'left';
			$temp_tpl=do_template('COMCODE_ALIGN',array('_GUID'=>'950b4d9db12cac6bf536860bedd96a36','ALIGN'=>$align,'CONTENT'=>$embed));
			break;
		case 'indent':
			if ($wml)
			{
				$temp_tpl=$embed;
				break;
			}

			$indent=array_key_exists('param',$attributes)?$attributes['param']:'10';
			if (!is_numeric($indent)) $indent='10';
			$temp_tpl=do_template('COMCODE_INDENT',array('_GUID'=>'d8e69fa17eebd5312e3ad5788e3a1343','INDENT'=>$indent,'CONTENT'=>$embed));
			break;
		case 'surround':
			if ($wml)
			{
				$temp_tpl=$embed;
				break;
			}

			if (($semiparse_mode) && ($embed->evaluate()=='')) // This is probably some signal like a break, so show it in Comcode form
			{
				$temp_tpl=make_string_tempcode('<kbd class="ocp_keep" title="no_parse">[surround="'.comcode_escape(array_key_exists('param',$attributes)?$attributes['param']:'float_surrounder').'"]'.$embed->evaluate().'[/surround]</kbd>');
				break;
			}

			$class=(array_key_exists('param',$attributes) && ($attributes['param']!=''))?$attributes['param']:'float_surrounder';
			$temp_tpl=do_template('COMCODE_SURROUND',array('_GUID'=>'e8e69fa17eebd5312e3ad5788e3a1343','CLASS'=>$class,'CONTENT'=>$embed));
			break;
		case 'i':
			if ($semiparse_mode)
			{
				$temp_tpl=make_string_tempcode('<i>'.$embed->evaluate().'</i>');
				break;
			}
			$temp_tpl=do_template('COMCODE_ITALICS',array('_GUID'=>'4321a1fe3825418e57a29410183c0c60','CONTENT'=>$embed));
			break;
		case 'u':
			if ($semiparse_mode)
			{
				$temp_tpl=make_string_tempcode('<u>'.$embed->evaluate().'</u>');
				break;
			}
			$temp_tpl=do_template('COMCODE_UNDERLINE',array('_GUID'=>'69cc8e73b17f9e6a35eb1af2bd1dc6ab','CONTENT'=>$embed));
			break;
		case 's':
			if ($wml)
			{
				$temp_tpl=$embed;
				break;
			}
			if ($semiparse_mode)
			{
				$temp_tpl=make_string_tempcode('<strike>'.$embed->evaluate().'</strike>');
				break;
			}

			$temp_tpl=do_template('COMCODE_STRIKE',array('_GUID'=>'ed242591cefd365497cc0c63abbb11a9','CONTENT'=>$embed));
			break;
		case 'tooltip':
			$param=is_object($attributes['param'])?$attributes['param']:comcode_to_tempcode($attributes['param'],$source_member,$as_admin,60,NULL,$connection,false,false,false,false,false,$highlight_bits,$on_behalf_of_member);

			if ($wml)
			{
				$temp_tpl->attach($embed);
				$temp_tpl->attach('[ ');
				$temp_tpl->attach($param);
				$temp_tpl->attach(' ]');
				break;
			}

			$temp_tpl=do_template('COMCODE_TOOLTIP',array('_GUID'=>'c9f4793dc0c1a92cd7d08ae1b87c2308','URL'=>array_key_exists('url',$attributes)?$attributes['url']:'','TOOLTIP'=>$param,'CONTENT'=>$embed));
			break;
		case 'sup':
			if ($wml)
			{
				$temp_tpl->attach('^');
				$temp_tpl->attach($embed);
				break;
			}

			$temp_tpl=do_template('COMCODE_SUP',array('_GUID'=>'74d2ecfe193dacb6d922bc288828196a','CONTENT'=>$embed));
			break;
		case 'sub':
			if ($wml)
			{
				$temp_tpl->attach('{');
				$temp_tpl->attach($embed);
				$temp_tpl->attach('}');
				break;
			}

			$temp_tpl=do_template('COMCODE_SUB',array('_GUID'=>'515e310e00a6d7c30f7dca0a5956ebcf','CONTENT'=>$embed));
			break;
		case 'title':
			if (($semiparse_mode) && (strpos($comcode,'[contents')!==false))
			{
				$temp_tpl=make_string_tempcode('[title'.reinsert_parameters($attributes).']'.$embed->evaluate().'[/title]');
				break;
			}

			$level=($attributes['param']!='')?intval($attributes['param']):1;
			if ($level==0) $level=1; // Stop crazy Comcode causing stack errors with the toc

			$uniq_id=strval(count($STRUCTURE_LIST));
			$STRUCTURE_LIST[]=array($level,$embed,$uniq_id);
			if ($level==1) $template='SCREEN_TITLE';
			elseif ($level==2) $template='COMCODE_SECTION_TITLE';
			elseif ($level==3) $template='COMCODE_MINOR_TITLE';
			elseif ($level==4) $template='COMCODE_VERY_MINOR_TITLE';
			else $template='COMCODE_VERY_MINOR_TITLE';
			if ($level==1)
			{
				if (is_null($COMCODE_PARSE_TITLE))
				{
					$COMCODE_PARSE_TITLE=$embed->evaluate();
					if (is_object($COMCODE_PARSE_TITLE)) $COMCODE_PARSE_TITLE=$COMCODE_PARSE_TITLE->evaluate();
				}
			}

			$base=array_key_exists('base',$attributes)?intval($attributes['base']):2;
			if ((array_key_exists('number',$attributes)) && ($level>=$base))
			{
				$list_types=($attributes['number']=='')?array():explode(',',$attributes['number']);
				$list_types+=array('decimal','lower-alpha','lower-roman','upper-alpha','upper-roman','disc');
				$numerals=array('i','ii','iii','iv','v','vi','viii','ix','x','xi','xii','xiii','xiv','xv','xvi','xvii','xviii','xix','xx');
				$symbol_lookup=array('decimal'=>range(1,100),'lower-alpha'=>range('a','z'),'lower-roman'=>$numerals,'upper-alpha'=>range('A','Z'),'upper-roman'=>str_replace('i','I',str_replace('v','V',str_replace('x','X',$numerals))));

				$level_text='';
				$list_pos=count($STRUCTURE_LIST)-2;
				for ($j=$level;$j>=$base;$j--)
				{
					$num_before=0;

					for ($i=$list_pos;$i>=0;$i--)
					{
						$list_pos--;

						if ($STRUCTURE_LIST[$i][0]==$j-1)
						{
							break;
						}
						if ($STRUCTURE_LIST[$i][0]==$j) $num_before++;
					}

					$level_number=strval($symbol_lookup[$list_types[$j-$base]][$num_before]);
					$level_text=$level_number.(($level_text!='')?'.':'').$level_text;
				}

				$old_embed=$embed;
				$embed=make_string_tempcode($level_text.' &ndash; ');
				$embed->attach($old_embed);
			}

			if ($wml)
			{
				if ($level==1)
				{
					$temp_tpl->attach('<br /><p><big><u><b>');
					$temp_tpl->attach($embed);
					$temp_tpl->attach('</b></u></big></p><br />');
				}
				elseif ($level==2)
				{
					$temp_tpl->attach('<br /><p><big><u>');
					$temp_tpl->attach($embed);
					$temp_tpl->attach('</u></big></p><br />');
				}
				elseif ($level==3)
				{
					$temp_tpl->attach('<br /><p><big>');
					$temp_tpl->attach($embed);
					$temp_tpl->attach('</big></p><br />');
				}
				elseif ($level==4)
				{
					$temp_tpl->attach('<br /><p>');
					$temp_tpl->attach($embed);
					$temp_tpl->attach('</p><br />');
				}
				break;
			}

			if ($semiparse_mode)
			{
				$temp_tpl=make_string_tempcode('<h'.strval($level).(($level==1)?' class="main_page_title"':'').'><span class="inner">'.$embed->evaluate().'</span></h'.strval($level).'>');
				break;
			}
			$tpl_map=array('ID'=>(substr($pass_id,0,5)=='panel')?NULL:$uniq_id,'TITLE'=>$embed,'HELP_URL'=>'','HELP_TERM'=>'');
			if (array_key_exists('sub',$attributes)) $tpl_map['SUB']=protect_from_escaping(comcode_to_tempcode($attributes['sub'],$source_member,$as_admin,60,NULL,$connection,false,false,false,false,false,$highlight_bits,$on_behalf_of_member));
			$temp_tpl=do_template($template,$tpl_map);
			break;
		case 'attachment':
		case 'attachment2': // legacy
		case 'attachment_safe':
			if ($wml)
			{
				break;
			}

			require_code('attachments');

			if (is_null($on_behalf_of_member)) $on_behalf_of_member=$source_member;

			$id=$embed->evaluate();
			global $COMCODE_ATTACHMENTS;

			if ((!is_numeric($id)) && (!$as_admin) && (!has_specific_permission($source_member,'exceed_filesize_limit')))
			{
				// We work all this out before we do any downloads, to make sure orphaned files aren't dumped on the file system (possible hack method)
				if (get_forum_type()=='ocf')
				{
					require_lang('ocf');
					require_code('ocf_groups');
					$daily_quota=ocf_get_member_best_group_property($source_member,'max_daily_upload_mb');
				} else
				{
					$daily_quota=5; // 5 is a hard coded default for non-OCF forums
				}
				if (!is_null($daily_quota))
				{
					$_size_uploaded_today=$connection->query('SELECT SUM(a_file_size) AS the_answer FROM '.$connection->get_table_prefix().'attachments WHERE a_member_id='.strval((integer)$source_member).' AND a_add_time>'.strval(time()-60*60*24));
					if (is_null($_size_uploaded_today[0]['the_answer'])) $_size_uploaded_today[0]['the_answer']=0;
					$size_uploaded_today=ceil(((float)$_size_uploaded_today[0]['the_answer'])/1024.0/1024.0);
					$attach_size=0;
					require_code('uploads');
					is_swf_upload(true);
					foreach ($_FILES as $_file)
						$attach_size+=floatval($_file['size'])/1024.0/1024.0;
					if (($size_uploaded_today+$attach_size)>floatval($daily_quota))
					{
						$temp_tpl=do_template('WARNING_TABLE',array('WARNING'=>do_lang_tempcode('OVER_DAILY_QUOTA',integer_format($daily_quota),float_format($size_uploaded_today))));
						break;
					}
				}
			}

			$thumb_url=array_key_exists('thumb_url',$attributes)?$attributes['thumb_url']:'';

			// Embedded attachments
			if ((!is_numeric($id)) && (substr($id,0,4)!='new_') && (substr($id,0,4)!='url_'))
			{
				$file=base64_decode(str_replace(chr(10),'',$id));
				if ($file===false)
				{
					$temp_tpl=do_template('WARNING_TABLE',array('WARNING'=>do_lang_tempcode('comcode:CORRUPT_ATTACHMENT')));
					break;
				}
				$md5=md5(substr($file,0,30));
				$original_filename=array_key_exists('filename',$attributes)?$attributes['filename']:($md5.'.dat');
				if (get_file_extension($original_filename)!='dat')
				{
					require_code('files2');
					check_extension($original_filename,true);
					$new_filename=$md5.'.'.get_file_extension($original_filename).'.dat';
				} else
				{
					$new_filename=$md5.'.'.get_file_extension($original_filename);
				}
				$path=get_custom_file_base().'/uploads/attachments/'.$new_filename;
				$myfile=@fopen($path,'wb');
				if ($myfile===false)
				{
					$temp_tpl=do_template('WARNING_TABLE',array('WARNING'=>intelligent_write_error_inline($path)));
					break;
				}
				if (fwrite($myfile,$file)<strlen($file)) warn_exit(do_lang_tempcode('COULD_NOT_SAVE_FILE'));
				fclose($myfile);
				fix_permissions($path);
				sync_file($path);
				$_size=strlen($file);
				$url='uploads/attachments/'.$new_filename;
				if ($connection->connection_write!=$GLOBALS['SITE_DB']->connection_write) $url=get_custom_base_url().'/'.$url;

				// Thumbnail
				if ($thumb_url=='')
				{
					require_code('images');
					if (is_image($original_filename))
					{
						$gd=((get_option('is_on_gd')=='1') && (function_exists('imagetypes')));
						if ($gd)
						{
							require_code('images');
							if (!is_saveable_image($url)) $ext='.png'; else $ext='.'.get_file_extension($original_filename);
							$thumb_url='uploads/attachments_thumbs/'.$md5.$ext;
							convert_image(get_custom_base_url().'/'.$url,get_custom_file_base().'/'.$thumb_url,-1,-1,intval(get_option('thumb_width')),true,NULL,false,true);

							if ($connection->connection_write!=$GLOBALS['SITE_DB']->connection_write) $thumb_url=get_custom_base_url().'/'.$thumb_url;
						} else $thumb_url=$url;
					}
				}

				if (addon_installed('galleries'))
				{
					require_code('images');
					if ((is_video($url)) && ($connection->connection_read==$GLOBALS['SITE_DB']->connection_read))
					{
						require_code('transcoding');
						$url=transcode_video($url,'attachments','a_url','a_original_filename',NULL,NULL);
					}
				}

				$attachment=array(
					'a_member_id'=>$on_behalf_of_member,
					'a_file_size'=>$_size,
					'a_url'=>$url,
					'a_thumb_url'=>$thumb_url,
					'a_original_filename'=>$original_filename,
					'a_num_downloads'=>0,
					'a_last_downloaded_time'=>NULL,
					'a_add_time'=>time());

				$attachment['a_description']=array_key_exists('description',$attributes)?(is_object($attributes['description'])?('[html]'.$attributes['description']->evaluate().'[/html]'):$attributes['description']):'';

				$attach_id=$connection->query_insert('attachments',$attachment,true);
				$attachment['id']=$attach_id;

				// Create and document attachment
				if (!array_key_exists('type',$attributes)) $attributes['type']='auto';
				$COMCODE_ATTACHMENTS[$pass_id][]=array('tag_type'=>$tag,'type'=>'new','attachmenttype'=>$attributes['type'],'description'=>$attachment['a_description'],'id'=>intval($attach_id),'marker'=>$marker,'comcode'=>$comcode); // Marker will allow us to search back and replace this with the added id
			}

			// New attachments
			elseif (!is_numeric($id))
			{
				require_code('uploads');

				if (substr($id,0,4)=='new_')
				{
					$_id=substr($id,4);
					if (!is_numeric($_id))
					{
						$temp_tpl=do_template('WARNING_TABLE',array('WARNING'=>do_lang_tempcode('comcode:INVALID_ATTACHMENT')));
						break;
					}

					$attributes['type']=post_param('attachmenttype'.$_id,array_key_exists('type',$attributes)?$attributes['type']:'auto');
					if (substr($attributes['type'],-8)=='_extract') $attributes['type']=substr($attributes['type'],0,strlen($attributes['type'])-8);

					$urls=get_url('','file'.$_id,'uploads/attachments',2,OCP_UPLOAD_ANYTHING,((!array_key_exists('thumb',$attributes)) || ($attributes['thumb']!='0')) && ($thumb_url==''),'','',true,true,true);
					if ($urls[0]=='') return new ocp_tempcode();//warn_exit(do_lang_tempcode('ERROR_UPLOADING'));  Can't do this, because this might not be post-calculated if something went wrong once
					is_swf_upload(true);
					$_size=$_FILES['file'.$_id]['size'];
					$original_filename=$_FILES['file'.$_id]['name'];
					if (get_magic_quotes_gpc()) $original_filename=stripslashes($original_filename);
				}
				elseif (substr($id,0,4)=='url_')
				{
					if ((!has_specific_permission($source_member,'draw_to_server')) && (!$as_admin)) break;

					$_id='!';
					$attributes['type']=post_param('attachmenttype'.$_id,array_key_exists('type',$attributes)?$attributes['type']:'auto');
					$url=remove_url_mistakes(substr($id,4));

					$_POST['_specify_url']=$url; // Little hack, as we need to read it from a POST
					if (get_magic_quotes_gpc()) $_POST['_specify_url']=addslashes($_POST['_specify_url']);
					$urls=get_url('_specify_url','','uploads/filedump',1,OCP_UPLOAD_ANYTHING,((!array_key_exists('thumb',$attributes)) || ($attributes['thumb']!='0')) && ($thumb_url==''),'','',true);
					$original_filename=rawurldecode(substr($url,strrpos($url,'/')+1));

					if (url_is_local($urls[0]))
					{
						$_size=@filesize(get_custom_file_base().'/'.rawurldecode($urls[0]));
						if ($_size===false) $_size=filesize(get_file_base().'/'.rawurldecode($urls[0]));
					} else $_size=0;
				}
				else
				{
					$temp_tpl=do_template('WARNING_TABLE',array('WARNING'=>do_lang_tempcode('comcode:INVALID_ATTACHMENT')));
					break;
				}

				if ($urls[0]=='')
				{
					require_code('images');
					require_code('files2');
					$temp_tpl=do_template('WARNING_TABLE',array('WARNING'=>do_lang_tempcode('ATTACHMENT_WOULD_NOT_UPLOAD',float_format(get_max_file_size()/1024/1024),float_format(get_max_image_size()/1024/1024))));
					break;
				}
				$url=$urls[0];
				if ($connection->connection_write!=$GLOBALS['SITE_DB']->connection_write) $url=get_custom_base_url().'/'.$url;
				if ($thumb_url=='')
					$thumb_url=array_key_exists(1,$urls)?$urls[1]:'';
				if (($thumb_url!='') && ($connection!=$GLOBALS['SITE_DB'])) $thumb_url=get_custom_base_url().'/'.$thumb_url;
				$num_downloads=0;
				$last_downloaded_time=NULL;
				$add_time=time();
				$member_id=$on_behalf_of_member;

				if (addon_installed('galleries'))
				{
					require_code('images');
					if ((is_video($url)) && ($connection->connection_read==$GLOBALS['SITE_DB']->connection_read))
					{
						require_code('transcoding');
						$url=transcode_video($url,'attachments','a_url','a_original_filename',NULL,NULL);
					}
				}

				$attachment=array(
					'a_member_id'=>$member_id,
					'a_file_size'=>$_size,
					'a_url'=>$url,
					'a_thumb_url'=>$thumb_url,
					'a_original_filename'=>$original_filename,
					'a_num_downloads'=>$num_downloads,
					'a_last_downloaded_time'=>$last_downloaded_time,
					'a_add_time'=>$add_time);

				$attachment['a_description']=post_param('caption'.$_id,array_key_exists('description',$attributes)?(is_object($attributes['description'])?('[html]'.$attributes['description']->evaluate().'[/html]'):$attributes['description']):'');

				$attach_id=$connection->query_insert('attachments',$attachment,true);
				$attachment['id']=$attach_id;

				if (($tag=='attachment2') || ($tag=='attachment_safe') || (substr($id,0,4)=='url_')) // Lock it if we are starting with this tag
				{
					$connection->query_delete('attachment_refs',array('r_referer_type'=>'null','r_referer_id'=>'','a_id'=>$attachment['id']),'',1);
					$connection->query_insert('attachment_refs',array('r_referer_type'=>'null','r_referer_id'=>'','a_id'=>$attachment['id']));
				}

				// Create and document attachment
				$COMCODE_ATTACHMENTS[$pass_id][]=array('tag_type'=>$tag,'time'=>time(),'type'=>(substr($id,0,4)=='new_')?'new':'url','attachmenttype'=>$attributes['type'],'description'=>$attachment['a_description'],'id'=>intval($attach_id),'marker'=>$marker,'comcode'=>$comcode); // Marker will allow us to search back and replace this with the added id

			// Existing attachments
			} else
			{
				$__id=intval($id);

				// Check we have permission to re-use this
				$owner=$connection->query_value_null_ok('attachments','a_member_id',array('id'=>$__id));
				if (is_null($owner)) // Missing attachment!
				{
					$temp_tpl=do_template('WARNING_TABLE',array('WARNING'=>do_lang_tempcode('MISSING_RESOURCE_COMCODE','attachment',escape_html(strval($__id)))));
					if ((!in_array(get_page_name(),$GLOBALS['DONT_CARE_MISSING_PAGES'])) && (!running_script('iframe')))
					{
						require_code('failure');
						relay_error_notification(do_lang('MISSING_RESOURCE_COMCODE','attachment',strval($__id)),false,$GLOBALS['FORUM_DRIVER']->is_staff($source_member)?'error_occurred_missing_reference_important':'error_occurred_missing_reference');
					}
					break;
				}

				$_attachment=$connection->query_select('attachments',array('*'),array('id'=>$__id),'',1);
				$attachment=$_attachment[0];

				$already_referenced=array_key_exists($__id,$GLOBALS['ATTACHMENTS_ALREADY_REFERENCED']);
				if (($already_referenced) || ($as_admin) || (/*(!is_guest($source_member)) && */($source_member===$owner)) || (((has_specific_permission($source_member,'reuse_others_attachments')) || ($owner==$source_member)) && (has_attachment_access($source_member,$__id))))
				{
					if (!array_key_exists('type',$attributes)) $attributes['type']='auto';
					$COMCODE_ATTACHMENTS[$pass_id][]=array('tag_type'=>$tag,'time'=>$attachment['a_add_time'],'type'=>'existing','id'=>$__id,'attachmenttype'=>$attributes['type'],'marker'=>$marker,'comcode'=>$comcode);
				} else
				{
					require_lang('permissions');
					$temp_tpl=do_template('WARNING_TABLE',array('WARNING'=>do_lang_tempcode('permissions:ACCESS_DENIED__REUSE_ATTACHMENT',$GLOBALS['FORUM_DRIVER']->get_username($source_member))));
					break;
					//access_denied('REUSE_ATTACHMENT');
				}

				if ($connection->connection_write!=$GLOBALS['SITE_DB']->connection_write)
				{
					if (url_is_local($attachment['a_url'])) $attachment['a_url']=get_custom_base_url().'/'.$attachment['a_url'];
					if (url_is_local($attachment['a_url'])) $attachment['a_thumb_url']=get_custom_base_url().'/'.$attachment['a_thumb_url'];
				}

				$attachment['a_description']=array_key_exists('description',$attributes)?(is_object($attributes['description'])?('[html]'.$attributes['description']->evaluate().'[/html]'):$attributes['description']):$attachment['a_description'];
			}

			// Now, render it
			// ==============
			$temp_tpl=render_attachment($tag,$attributes,$attachment,$pass_id,$source_member,$as_admin,$connection,$highlight_bits,$on_behalf_of_member,$semiparse_mode);

			if (array_key_exists('float',$attributes)) $temp_tpl=do_template('FLOATER',array('_GUID'=>'802fe29019be80993296de7cc8b5cc5e','FLOAT'=>$attributes['float'],'CONTENT'=>$temp_tpl));

			break;
		case 'include':
			$codename=$embed->evaluate();
			$zone=$attributes['param'];
			if ($zone=='_SEARCH') $zone=get_comcode_zone($codename);
			if ($zone=='_SELF') $zone=get_zone_name();

			$temp_comcode_parse_title=$COMCODE_PARSE_TITLE;
			$temp=request_page($codename,false,$zone,NULL,true);
			$COMCODE_PARSE_TITLE=$temp_comcode_parse_title;
			if ($temp->is_empty())
			{
				$temp_tpl=do_template('WARNING_TABLE',array('WARNING'=>do_lang_tempcode('MISSING_RESOURCE_COMCODE','include',hyperlink(build_url(array('page'=>'cms_comcode_pages','type'=>'_ed','page_link'=>$zone.':'.$codename),get_module_zone('cms_comcode_pages')),$zone.':'.$codename,false,true))));
				if ((!in_array(get_page_name(),$GLOBALS['DONT_CARE_MISSING_PAGES'])) && (!running_script('iframe')))
				{
					require_code('failure');
					relay_error_notification(do_lang('MISSING_RESOURCE_COMCODE','include',$zone.':'.$codename),false,$GLOBALS['FORUM_DRIVER']->is_staff($source_member)?'error_occurred_missing_reference_important':'error_occurred_missing_reference');
				}
			} else
			{
				$temp_tpl=symbol_tempcode('LOAD_PAGE',array($codename,$zone));
			}
			break;
		case 'random':
			unset($attributes['param']);

			if ($wml)
			{
				$top_attribute=array_pop($attributes);
				$temp_tpl=is_object($top_attribute)?$top_attribute:comcode_to_tempcode($top_attribute,$source_member,$as_admin,60,NULL,$connection,false,false,false,false,false,$highlight_bits,$on_behalf_of_member);
				break;
			}

			$max=($embed->evaluate()=='')?intval($embed->evaluate()):0;
			foreach ($attributes as $num=>$val)
			{
				$_temp=is_object($val)?$val:comcode_to_tempcode($val,$source_member,$as_admin,60,NULL,$connection,false,false,false,false,false,$highlight_bits,$on_behalf_of_member);
				$attributes[$num]=$_temp->evaluate();
				if (intval($num)>$max) $max=intval($num);
			}

			$_parts=new ocp_tempcode();
			krsort($attributes);
			foreach ($attributes as $num=>$val)
			{
				$_parts->attach(do_template('COMCODE_RANDOM_PART',array('_GUID'=>'5fa49a916304f9caa0ddedeb01531142','NUM'=>strval($num),'VAL'=>$val)));
			}

			$temp_tpl=do_template('COMCODE_RANDOM',array('_GUID'=>'9b77aaf593b12c763fb0c367fab415b6','UNIQID'=>uniqid(''),'FULL'=>$embed,'MAX'=>strval($max),'PARTS'=>$_parts));
			break;
		case 'jumping':
			unset($attributes['param']);

			if ($wml)
			{
				$top_attribute=array_pop($attributes);
				$temp_tpl=is_object($top_attribute)?$top_attribute:comcode_to_tempcode($top_attribute,$source_member,$as_admin,60,NULL,$connection,false,false,false,false,false,$highlight_bits,$on_behalf_of_member);
				break;
			}

			$_parts=new ocp_tempcode();
			foreach ($attributes as $val)
			{
				$_temp=is_object($val)?$val:comcode_to_tempcode($val,$source_member,$as_admin,60,NULL,$connection,false,false,false,false,false,$highlight_bits,$on_behalf_of_member);
				$_parts->attach(do_template('COMCODE_JUMPING_PART',array('_GUID'=>'d163bd11920f39f0cb8ff2f6ba48bc80','PART'=>$_temp->evaluate())));
			}

			$embed=$embed->evaluate();
			$temp_tpl=do_template('COMCODE_JUMPING',array('_GUID'=>'85e9f83ed134868436a7db7692f56047','UNIQID'=>uniqid(''),'FULL'=>implode(', ',$attributes),'TIME'=>strval((integer)$embed),'PARTS'=>$_parts));
			break;
		case 'shocker':
			if ($wml)
			{
				$top_attribute=array_pop($attributes);
				$temp_tpl=is_object($top_attribute)?$top_attribute:comcode_to_tempcode($top_attribute,$source_member,$as_admin,60,NULL,$connection,false,false,false,false,false,$highlight_bits,$on_behalf_of_member);
				break;
			}

			$_parts=new ocp_tempcode();
			foreach ($attributes as $key=>$val)
			{
				if (substr($key,0,5)=='left_')
				{
					$left=$val;
					$right=array_key_exists('right_'.substr($key,5),$attributes)?$attributes['right_'.substr($key,5)]:'';

					$left=is_object($left)?$left:comcode_to_tempcode($left,$source_member,$as_admin,60,NULL,$connection,false,false,false,false,false,$highlight_bits,$on_behalf_of_member);
					$right=is_object($right)?$right:comcode_to_tempcode($right,$source_member,$as_admin,60,NULL,$connection,false,false,false,false,false,$highlight_bits,$on_behalf_of_member);
					$_parts->attach(do_template('COMCODE_SHOCKER_PART',array('LEFT'=>$left,'RIGHT'=>$right)));
				}
			}

			$min_color=array_key_exists('min',$attributes)?$attributes['min']:'0000FF';
			$max_color=array_key_exists('max',$attributes)?$attributes['max']:'FF0044';
			if (substr($min_color,0,1)=='#') $min_color=substr($min_color,1);
			if (substr($max_color,0,1)=='#') $max_color=substr($max_color,1);

			$embed=$embed->evaluate();
			$temp_tpl=do_template('COMCODE_SHOCKER',array('UNIQID'=>uniqid(''),'MIN_COLOR'=>$min_color,'MAX_COLOR'=>$max_color,'FULL'=>implode(', ',$attributes),'TIME'=>strval((integer)$embed),'PARTS'=>$_parts));
			break;
		case 'ticker':
			if ($wml)
			{
				$temp_tpl=$embed;
				break;
			}

			$width=$attributes['param'];
			if (!is_numeric($width)) $width='300';
			$fspeed=array_key_exists('speed',$attributes)?float_to_raw_string(floatval($attributes['speed'])):'1';
			$temp_tpl=do_template('COMCODE_TICKER',array('_GUID'=>'e48893cda61995261577f0556443c537','UNIQID'=>uniqid(''),'SPEED'=>$fspeed,'WIDTH'=>$width,'TEXT'=>$embed));

			break;
		case 'highlight':
			if ($wml)
			{
				$temp_tpl->attach('<i>');
				$temp_tpl->attach($embed);
				$temp_tpl->attach('</i>');
				break;
			}

			$temp_tpl=do_template('COMCODE_HIGHLIGHT',array('_GUID'=>'695d041b6605f06ec2aeee1e82f87185','CONTENT'=>$embed));
			break;
		case 'size':
			$size=array_key_exists('param',$attributes)?($attributes['param']):'1';

			if ($wml)
			{
				if (floatval($size)>=1.5)
				{
					$temp_tpl->attach('<big>');
					$temp_tpl->attach($embed);
					$temp_tpl->attach('</big>');
				} elseif (floatval($size)<0.8)
				{
					$temp_tpl->attach('<small>');
					$temp_tpl->attach($embed);
					$temp_tpl->attach('</small>');
				} else
				{
					$temp_tpl->attach($embed);
				}
				break;
			}

			if (is_numeric($size))
			{
				$size='font-size: '.$size.'em;';
			} elseif (substr($size,0,1)=='+')
			{
				$size='font-size: '.substr($size,1).'em';
			} elseif (substr($size,-1)=='%')
			{
				$size='font-size: '.float_to_raw_string(floatval(substr($size,0,strlen($size)-1))/100.0).'em';
			} elseif (substr($size,-2)=='of')
			{
				$new_size='1em';
				switch ($size)
				{
					case '1of':
						$new_size='8pt';
						break;
					case '2of':
						$new_size='10pt';
						break;
					case '3of':
						$new_size='12pt';
						break;
					case '4of':
						$new_size='14pt';
						break;
					case '5of':
						$new_size='18pt';
						break;
					case '6of':
						$new_size='24pt';
						break;
					case '7of':
						$new_size='36pt';
						break;
				}
				$size='font-size: '.$new_size;
			} else
			{
				$size='font-size: '.$size;
			}

			$size_len=strlen($size);
			filter_html($as_admin,$source_member,0,$size_len,$size,false,false);
			$temp_tpl=do_template('COMCODE_FONT',array('_GUID'=>'fb23fdcb45aabdfeca9f37ed8098948e','CONTENT'=>$embed,'SIZE'=>$size,'COLOR'=>'','FACE'=>''));
			break;
		case 'color':
			if ($wml)
			{
				$temp_tpl=$embed;
				break;
			}

			$color=array_key_exists('param',$attributes)?('color: '.$attributes['param'].';'):'';
			$temp_tpl=do_template('COMCODE_FONT',array('_GUID'=>'bd146414c9239ba2076f4b683df437d7','CONTENT'=>$embed,'SIZE'=>'','COLOR'=>$color,'FACE'=>''));
			$color_len=strlen($color);
			filter_html($as_admin,$source_member,0,$color_len,$color,false,false);
			break;
		case 'tt':
			if ($wml)
			{
				$temp_tpl->attach('<i>');
				$temp_tpl->attach($embed);
				$temp_tpl->attach('</i>');
				break;
			}

			$temp_tpl=do_template('COMCODE_TELETYPE',array('CONTENT'=>$embed));
			break;
		case 'samp':
			if ($wml)
			{
				$temp_tpl->attach('<i>');
				$temp_tpl->attach($embed);
				$temp_tpl->attach('</i>');
				break;
			}

			$temp_tpl=do_template('COMCODE_SAMP',array('CONTENT'=>$embed));
			break;
		case 'q':
			if ($wml)
			{
				$temp_tpl->attach('<i>');
				$temp_tpl->attach($embed);
				$temp_tpl->attach('</i>');
				break;
			}

			$temp_tpl=do_template('COMCODE_Q',array('CONTENT'=>$embed));
			break;
		case 'var':
			if ($wml)
			{
				$temp_tpl->attach('<i>');
				$temp_tpl->attach($embed);
				$temp_tpl->attach('</i>');
				break;
			}

			$temp_tpl=do_template('COMCODE_VAR',array('CONTENT'=>$embed));
			break;
		case 'font':
			$face=$attributes['param'];
			if (($face=='') && (array_key_exists('face',$attributes))) $face=$attributes['face'];
			$color=array_key_exists('color',$attributes)?$attributes['color']:'';
			$size=array_key_exists('size',$attributes)?$attributes['size']:'';
			if ($face=='/') $face='';
			if ($color=='/') $color='';
			if ($size=='/') $size='';

			if ($wml)
			{
				$before='';
				$after='';
				if ($size!='')
				{
					if (floatval($size)>=1.5)
					{
						$before='<big>';
						$after='</big>';
					} elseif (floatval($size)<0.8)
					{
						$before='<small>';
						$after='</small>';
					}
				}

				$temp_tpl->attach($before);
				$temp_tpl->attach($embed);
				$temp_tpl->attach($after);
				break;
			}

			if ($color!='') $color='color: '.$color.';';
			if ($size!='')
			{
				if (is_numeric($size))
				{
					$size='font-size: '.$size.'em;';
				} elseif (substr($size,0,1)=='+')
				{
					$size='font-size: '.substr($size,1).'em';
				} elseif (substr($size,-1)=='%')
				{
					$size='font-size: '.float_to_raw_string(floatval(substr($size,0,strlen($size)-1))/100.0).'em';
				} elseif (substr($size,-2)=='of')
				{
					$new_size='1em';
					switch ($size)
					{
						case '1of':
							$new_size='8pt';
							break;
						case '2of':
							$new_size='10pt';
							break;
						case '3of':
							$new_size='12pt';
							break;
						case '4of':
							$new_size='14pt';
							break;
						case '5of':
							$new_size='18pt';
							break;
						case '6of':
							$new_size='24pt';
							break;
						case '7of':
							$new_size='36pt';
							break;
					}
					$size='font-size: '.$new_size;
				} else
				{
					$size='font-size: '.$size;
				}
			}
			if ($face!='') $face='font-family: '.str_replace('\'','',$face).';';
			$size_len=strlen($size);
			filter_html($as_admin,$source_member,0,$size_len,$size,false,false);
			$color_len=strlen($color);
			filter_html($as_admin,$source_member,0,$color_len,$color,false,false);
			$face_len=strlen($face);
			filter_html($as_admin,$source_member,0,$face_len,$face,false,false);
			$temp_tpl=do_template('COMCODE_FONT',array('_GUID'=>'f5fcafe737b8fdf466a6a51773e09c9b','CONTENT'=>$embed,'SIZE'=>$size,'COLOR'=>$color,'FACE'=>$face));
			break;
		case 'box':
			if ($wml)
			{
				$temp_tpl->attach('<br /><p>');
				if ($attributes['param']!='')
				{
					$temp_tpl->attach('<big>');
					$temp_tpl->attach($attributes['param']);
					$temp_tpl->attach('</big><br /><br />');
				}
				$temp_tpl->attach($embed);
				$temp_tpl->attach('</p></br />');
				break;
			}

			// Legacy parameter. There used to be 'place' and 'nowrap' and 'class', but these are now gone.
			$breadth=array_key_exists('breadth',$attributes)?$attributes['breadth']:'100%';
			if ($breadth=='WIDE') $breadth='100%';
			if ($breadth=='WIDE_HIGH') $breadth='100%';
			if ($breadth=='THIN') $breadth='auto';
			// The new versions
			$dimensions=array_key_exists('dimensions',$attributes)?comcode_to_tempcode($attributes['dimensions'],$source_member,$as_admin,60,NULL,$connection,false,false,false,false,false,$highlight_bits,$on_behalf_of_member):make_string_tempcode($breadth);
			$type=array_key_exists('type',$attributes)?$attributes['type']:'';
			$options=array_key_exists('options',$attributes)?$attributes['options']:'';
			$meta=($comcode_dangerous&&array_key_exists('meta',$attributes))?$attributes['meta']:''; //Insecure, unneeded here
			$links=($comcode_dangerous&&array_key_exists('links',$attributes))?$attributes['links']:''; //Insecure, unneeded here
			$converted=is_object($attributes['param'])?$attributes['param']:comcode_to_tempcode($attributes['param'],$source_member,$as_admin,60,NULL,$connection,false,false,false,false,false,$highlight_bits,$on_behalf_of_member);

			$temp_tpl=directive_tempcode('BOX',$embed,array($converted,$dimensions,make_string_tempcode($type),make_string_tempcode($options),make_string_tempcode($meta),make_string_tempcode($links)));
			if (array_key_exists('float',$attributes)) $temp_tpl=do_template('FLOATER',array('_GUID'=>'54e8fc9ec1e16cfc5c8824e22f1e8745','FLOAT'=>$attributes['float'],'CONTENT'=>$temp_tpl));
			break;
		case 'concept':
			if ($wml)
			{
				$temp_tpl=$embed;
				break;
			}

			if ((!array_key_exists('param',$attributes)) || ($attributes['param']==''))
			{
				$text=$embed->evaluate();

				$page=get_tutorial_link('concept__'.preg_replace('#[^\w_]#','_',$text));
				if (!is_null($page))
				{
					$zone=get_comcode_zone($page,false);
				}
				if ((is_null($page)) || (is_null($zone)))
				{
					$temp_tpl=make_string_tempcode($text);
				} else
				{
					$_url=build_url(array('page'=>$page),$zone);
					$_url->attach('#concept__'.preg_replace('#[^\w_]#','_',$text));
					$temp_tpl=do_template('COMCODE_CONCEPT',array('_GUID'=>'ee0cd05f87329923f05145180004d8a8','TEXT'=>$text,'URL'=>$_url));
				}
			} else
			{
				$temp_tpl=do_template('COMCODE_CONCEPT_INLINE',array('_GUID'=>'381a59de4d6f8967446c12bf4641a9ce','TEXT'=>$embed,'FULL'=>$attributes['param']));
			}
			break;
		case 'concepts':
			if ($wml)
			{
				break;
			}

			$title=$embed->evaluate();

			$concepts=new ocp_tempcode();
			foreach ($attributes as $_key=>$_value)
			{
				if (substr($_key,-4)=='_key')
				{
					$key=$_value;
					$cid=substr($_key,0,strlen($_key)-4);
					$to_parse=array_key_exists($cid.'_value',$attributes)?$attributes[$cid.'_value']:new ocp_tempcode();
					$value=is_object($to_parse)?$to_parse:comcode_to_tempcode($to_parse,$source_member,$as_admin,60,NULL,$connection,false,false,false,false,false,$highlight_bits,$on_behalf_of_member);
					$concepts->attach(do_template('COMCODE_CONCEPTS_CONCEPT',array('_GUID'=>'4baf6dabc32146c594c7fd922791b6b2','A'=>'concept__'.preg_replace('#[^\w]#','_',$_value),'KEY'=>$key,'VALUE'=>$value)));

					if ((get_param('type','')=='') && (get_param('id','',true)==''))
						set_tutorial_link('concept__'.preg_replace('#[^\w]#','_',$key),get_page_name());
				}
			}

			$temp_tpl=do_template('COMCODE_CONCEPTS',array('_GUID'=>'4c7a1d70753dc1d209b9951aa10f361a','TITLE'=>$title,'CONCEPTS'=>$concepts));
			break;
		case 'exp_ref':
			if ($wml)
			{
				break;
			}

			$_embed=$embed->evaluate();
			if (strpos($_embed,'.')!==false) break;
			$stub=get_file_base().'/data_custom/images/'.get_zone_name().'/';
			$stub2=get_base_url().'/data_custom/images/'.get_zone_name().'/';
			if (!file_exists($stub))
			{
				$stub=get_file_base().'/data/images/'.get_zone_name().'/';
				$stub2=get_base_url().'/data/images/'.get_zone_name().'/';
			}
			if (!file_exists($stub))
			{
				$stub=get_file_base().'/data_custom/images/';
				$stub2=get_base_url().'/data_custom/images/';
			}
			if (!file_exists($stub))
			{
				$stub=get_file_base().'/data/images/';
				$stub2=get_base_url().'/data/images/';
			}
			if (substr($_embed,0,1)=='/') $_embed=substr($_embed,1);
			if (file_exists($stub.$_embed.'.png')) $url=$stub2.$_embed.'.png';
			elseif (file_exists($stub.$_embed.'.gif')) $url=$stub2.$_embed.'.gif';
			elseif (file_exists($stub.$_embed.'.jpg')) $url=$stub2.$_embed.'.jpg';
			elseif (file_exists($stub.$_embed.'.jpeg')) $url=$stub2.$_embed.'.jpeg';
			else
			{
				$stub=get_file_base().'/data/images/docs/';
				$stub2=get_base_url().'/data/images/docs/';
				if (substr($_embed,0,1)=='/') $_embed=substr($_embed,1);
				if (file_exists($stub.$_embed.'.png')) $url=$stub2.$_embed.'.png';
				elseif (file_exists($stub.$_embed.'.gif')) $url=$stub2.$_embed.'.gif';
				elseif (file_exists($stub.$_embed.'.jpg')) $url=$stub2.$_embed.'.jpg';
				elseif (file_exists($stub.$_embed.'.jpeg')) $url=$stub2.$_embed.'.jpeg';
				else
				{
					$temp_tpl=do_template('WARNING_TABLE',array('WARNING'=>do_lang_tempcode('MISSING_RESOURCE_COMCODE','exp_ref',escape_html($_embed))));
					if (array_key_exists('COMCODE_BROKEN_URLS',$GLOBALS))
					{
						$GLOBALS['COMCODE_BROKEN_URLS'][]=array($_embed,NULL);
					} elseif ((!in_array(get_page_name(),$GLOBALS['DONT_CARE_MISSING_PAGES'])) && (!running_script('iframe')))
					{
						require_code('failure');
						relay_error_notification(do_lang('MISSING_RESOURCE_COMCODE','exp_ref',$_embed),false,$GLOBALS['FORUM_DRIVER']->is_staff($source_member)?'error_occurred_missing_reference_important':'error_occurred_missing_reference');
					}

					break;
				}
			}

			$text=make_string_tempcode($attributes['param']);
			if ($text->is_empty()) $text=do_lang_tempcode('EXAMPLE');

			$temp_tpl=do_template('COMCODE_EXP_REF',array('_GUID'=>'89e7f528e72096e3458d6acb70734d0b','TEXT'=>$text,'URL'=>$url));
			break;
		case 'exp_thumb':
			if ($wml)
			{
				break;
			}

			$_embed=$embed->evaluate();
			if (strpos($_embed,'.')!==false) break;
			$stub=get_file_base().'/data/images/'.get_zone_name().'/';
			$stub2=get_base_url().'/data/images/'.get_zone_name().'/';
			if (substr($_embed,0,1)=='/') $_embed=substr($_embed,1);
			if (file_exists($stub.$_embed.'.png')) $url_full=$stub2.$_embed.'.png';
			elseif (file_exists($stub.$_embed.'.gif')) $url_full=$stub2.$_embed.'.gif';
			elseif (file_exists($stub.$_embed.'.jpg')) $url_full=$stub2.$_embed.'.jpg';
			elseif (file_exists($stub.$_embed.'.jpeg')) $url_full=$stub2.$_embed.'.jpeg';
			else
			{
				$stub=get_file_base().'/data/images/docs/';
				$stub2=get_base_url().'/data/images/docs/';
				if (substr($_embed,0,1)=='/') $_embed=substr($_embed,1);
				if (file_exists($stub.$_embed.'.png')) $url_full=$stub2.$_embed.'.png';
				elseif (file_exists($stub.$_embed.'.gif')) $url_full=$stub2.$_embed.'.gif';
				elseif (file_exists($stub.$_embed.'.jpg')) $url_full=$stub2.$_embed.'.jpg';
				elseif (file_exists($stub.$_embed.'.jpeg')) $url_full=$stub2.$_embed.'.jpeg';
				else
				{
					$temp_tpl=do_template('WARNING_TABLE',array('WARNING'=>do_lang_tempcode('MISSING_RESOURCE_COMCODE','exp_thumb',escape_html($_embed))));
					if (array_key_exists('COMCODE_BROKEN_URLS',$GLOBALS))
					{
						$GLOBALS['COMCODE_BROKEN_URLS'][]=$_embed;
					} elseif ((!in_array(get_page_name(),$GLOBALS['DONT_CARE_MISSING_PAGES'])) && (!running_script('iframe')))
					{
						require_code('failure');
						relay_error_notification(do_lang('MISSING_RESOURCE_COMCODE','exp_thumb',$_embed),false,$GLOBALS['FORUM_DRIVER']->is_staff($source_member)?'error_occurred_missing_reference_important':'error_occurred_missing_reference');
					}
					break;
				}
			}

			$float=array_key_exists('float',$attributes)?$attributes['float']:'right';
			$text=$attributes['param'];

			if ((get_option('is_on_gd')=='0') || (!function_exists('imagetypes')))
			{
				$url_thumb=$url_full;
			} else
			{
				$new_name=$_embed.'_thumb.png';
				$file_thumb=$stub.$new_name;
				if (file_exists($file_thumb))
				{
					$url_thumb=$stub2.rawurlencode($new_name);
				} else
				{
					$new_name=$_embed.'.png';
					$file_thumb=get_custom_file_base().'/uploads/auto_thumbs/'.$new_name;
					if (!file_exists($file_thumb))
					{
						require_code('images');
						convert_image($url_full,$file_thumb,-1,-1,150,false);
					}
					$url_thumb=get_custom_base_url().'/uploads/auto_thumbs/'.rawurlencode($new_name);
				}
			}

			if (get_param_integer('wide_print',0)==1)
			{
				$temp_tpl=do_template('COMCODE_EXP_THUMB_PRINT',array('_GUID'=>'de7f8a7fa29c2335f381a0beb3da9406','FLOAT'=>$float,'TEXT'=>$text,'URL_THUMB'=>$url_thumb,'URL_FULL'=>$url_full));
			} else
			{
				$temp_tpl=do_template('COMCODE_EXP_THUMB',array('_GUID'=>'ce7f8a7fa29c2335f381a0beb3da9406','FLOAT'=>$float,'TEXT'=>$text,'URL_THUMB'=>$url_thumb,'URL_FULL'=>$url_full));
			}
			break;
		case 'thumb':
			if ($wml)
			{
				break;
			}

			$_embed=$embed->evaluate();
			$_embed=remove_url_mistakes($_embed);
			check_naughty_javascript_url($source_member,$_embed,$as_admin);
			if (substr($_embed,0,1)=='/') $_embed=substr($_embed,1);
			if (url_is_local($_embed))
			{
				if ((file_exists(get_file_base().'/'.$_embed)) && (!file_exists(get_custom_file_base().'/'.$_embed)))
				{
					$url_full=get_base_url().'/'.$_embed;
				} else
				{
					$url_full=get_custom_base_url().'/'.$_embed;
				}
			} else
			{
				$url_full=$_embed;
			}
			$align=array_key_exists('align',$attributes)?$attributes['align']:'bottom';
			if ((get_option('is_on_gd')=='0') || (!function_exists('imagetypes')) || ((!has_specific_permission($source_member,'draw_to_server')) && (!$as_admin)))
			{
				$url_thumb=$url_full;
			} else
			{
				if ($attributes['param']!='')
				{
					$url_thumb=url_is_local($attributes['param'])?get_custom_base_url().'/'.$attributes['param']:$attributes['param'];
				}
				if (($attributes['param']=='') || ((url_is_local($attributes['param'])) && (!file_exists(get_custom_file_base().'/'.rawurldecode($attributes['param'])))))
				{
					$new_name=url_to_filename($url_full);
					require_code('images');
					if (!is_saveable_image($new_name)) $new_name.='.png';
					if (is_null($new_name))
					{
						$temp_tpl=do_template('WARNING_TABLE',array('WARNING'=>do_lang_tempcode('URL_THUMB_TOO_LONG')));
						break;
					}
					$file_thumb=get_custom_file_base().'/uploads/auto_thumbs/'.$new_name;
					if ((!file_exists($file_thumb)) && (strpos($file_thumb,'{$')===false))
					{
						convert_image($url_full,$file_thumb,-1,-1,intval(get_option('thumb_width')),false);
					}
					$url_thumb=get_custom_base_url().'/uploads/auto_thumbs/'.rawurlencode($new_name);
				}
			}
			$caption=array_key_exists('caption',$attributes)?$attributes['caption']:'';
			$temp_tpl=do_template('COMCODE_THUMB',array('_GUID'=>'1b0d25f72ef5f816091269e29c586d60','CAPTION'=>$caption,'RAND'=>strval(mt_rand(0,32000)),'ALIGN'=>$align,'PASS_ID'=>(intval($pass_id)<0)?strval(mt_rand(0,10000)):$pass_id,'URL_THUMB'=>$url_thumb,'URL_FULL'=>$url_full));

			if (array_key_exists('float',$attributes)) $temp_tpl=do_template('FLOATER',array('_GUID'=>'cbc56770714a44f56676f43da282cc7a','FLOAT'=>$attributes['float'],'CONTENT'=>$temp_tpl));
			break;
		case 'img':
			if ($wml)
			{
				break;
			}
			if (($semiparse_mode) && (array_key_exists('rollover',$attributes)))
			{
				$temp_tpl=make_string_tempcode('[img'.reinsert_parameters($attributes).']'.$embed->evaluate().'[/img]');
				break;
			}

			$_embed=$embed->evaluate();
			$given_url=$_embed;
			$_embed=remove_url_mistakes($_embed);
			if (substr($_embed,0,1)=='/') $_embed=substr($_embed,1);
			check_naughty_javascript_url($source_member,$_embed,$as_admin);
			if (url_is_local($_embed))
			{
				if ((file_exists(get_file_base().'/'.$_embed)) && (!file_exists(get_custom_file_base().'/'.$_embed)))
				{
					$url_full=get_base_url().'/'.$_embed;
				} else
				{
					$url_full=get_custom_base_url().'/'.$_embed;
				}
			} else
			{
				$url_full=$_embed;
			}
			$temp_tpl=test_url($url_full,'img',@html_entity_decode($given_url,ENT_QUOTES,get_charset()),$source_member);
			$align=array_key_exists('align',$attributes)?$attributes['align']:'';
			$caption=is_object($attributes['param'])?$attributes['param']:comcode_to_tempcode($attributes['param'],$source_member,$as_admin,60,NULL,$connection,false,false,false,false,false,$highlight_bits,$on_behalf_of_member);
			if (array_key_exists('title',$attributes))
			{
				$tooltip=is_object($attributes['title'])?$attributes['title']:comcode_to_tempcode($attributes['title'],$source_member,$as_admin,60,NULL,$connection,false,false,false,false,false,$highlight_bits,$on_behalf_of_member);
			} else
			{
				$tooltip=$caption;
			}
			$rollover=array_key_exists('rollover',$attributes)?$attributes['rollover']:NULL;
			if ((!is_null($rollover)) && (url_is_local($rollover)))
			{
				if ((file_exists(get_file_base().'/'.$rollover)) && (!file_exists(get_custom_file_base().'/'.$rollover)))
				{
					$rollover=get_base_url().'/'.$rollover;
				} else
				{
					$rollover=get_custom_base_url().'/'.$rollover;
				}
			}
			$refresh_time=array_key_exists('refresh_time',$attributes)?strval(intval($attributes['refresh_time'])):'0';
			$temp_tpl->attach(do_template('COMCODE_IMG',array('_GUID'=>'70166d8dbb0aff064b99c0dd30ed77a8','RAND'=>uniqid(''),'REFRESH_TIME'=>$refresh_time,'ROLLOVER'=>$rollover,'ALIGN'=>$align,'URL'=>$url_full,'TOOLTIP'=>$tooltip,'CAPTION'=>$caption)));

			if (array_key_exists('float',$attributes)) $temp_tpl=do_template('FLOATER',array('_GUID'=>'918162250c80e10212efd9a051545b9b','FLOAT'=>$attributes['float'],'CONTENT'=>$temp_tpl));

			break;
		case 'flash':
			if ($wml)
			{
				break;
			}

			$_embed=$embed->evaluate();
			$given_url=$_embed;
			$_embed=remove_url_mistakes($_embed);
			if (substr($_embed,0,1)=='/') $_embed=substr($_embed,1);
			check_naughty_javascript_url($source_member,$_embed,$as_admin);
			$url_full=url_is_local($_embed)?(get_custom_base_url().'/'.$_embed):$_embed;
			$temp_tpl=test_url($url_full,'flash',@html_entity_decode($given_url,ENT_QUOTES,get_charset()),$source_member);
			if (($attributes['param']=='') || (strpos($attributes['param'],'x')===false))
			{
				if (!array_key_exists('width',$attributes)) $attributes['width']='300';
				if (!array_key_exists('height',$attributes)) $attributes['height']='300';
				$attributes['param']=$attributes['width'].'x'.$attributes['height'];
			}
			list($width,$height)=explode('x',$attributes['param'],2);
			if ((addon_installed('jwplayer')) && ((substr($url_full,-4)=='.flv') || (substr($url_full,-4)=='.mp4') || (substr($url_full,-4)=='.mp3') || (substr($url_full,-4)=='.webm')))
			{
				$temp_tpl->attach(do_template('COMCODE_FLV',array('_GUID'=>'4746684d9e098709cc6671e1b00ce47e','URL'=>$url_full,'WIDTH'=>$width,'HEIGHT'=>$height)));
			} else
			{
				$temp_tpl->attach(do_template('COMCODE_SWF',array('_GUID'=>'8bc61ad75977a5a85eff96454af31fe8','URL'=>$url_full,'WIDTH'=>$width,'HEIGHT'=>$height)));
			}
			break;
		case 'url':
			// Make them both HTML strings
			$url=$embed->evaluate();
			if (is_object($attributes['param']))
			{
				$switch_over=true; // We know if must be Comcode XML
				$attributes['param']=$attributes['param']->evaluate();
			} else
			{
				$switch_over=((!looks_like_url($url)) && (looks_like_url($attributes['param'],true)));
				if ((strpos($attributes['param'],'[')!==false) || (strpos($attributes['param'],'{')!==false)) // Extra Comcode parsing wanted?
				{
					$param_temp=comcode_to_tempcode(escape_html($attributes['param']),$source_member,$as_admin,60,NULL,$connection,false,false,true,false,false,$highlight_bits,$on_behalf_of_member);
					global $ADVERTISING_BANNERS;
					$temp_ab=$ADVERTISING_BANNERS;
					$ADVERTISING_BANNERS=array();
					$caption=$param_temp;
					$ADVERTISING_BANNERS=$temp_ab;
				} else
				{
					$caption=make_string_tempcode(escape_html($attributes['param'])); // Consistency of escaping
				}
			}

			// Do we need to switch around?
			if ($switch_over)
			{
				$url=$attributes['param'];
				$caption=$embed;
			}

			// If we weren't given a caption, use the URL, but crop if necessary
			if ($caption->evaluate()=='')
			{
				$_caption=$url;

				// Shorten the URL if it is too long
				$max_link_length=50;
				if (strlen($_caption)>$max_link_length)
				{
					$_caption=escape_html(substr(@html_entity_decode($_caption,ENT_QUOTES,get_charset()),0,intval($max_link_length/2-3))).'&hellip;'.escape_html(substr(@html_entity_decode($_caption,ENT_QUOTES,get_charset()),intval(-$max_link_length/2)));
				}

				$caption=make_string_tempcode($_caption);
			}

			// Tidy up the URL now
			$url=@html_entity_decode($url,ENT_QUOTES,get_charset());
			$url=fixup_protocolless_urls($url);

			// Integrity and security
			check_naughty_javascript_url($source_member,$url,$as_admin);

			// More URL tidying
			$local=(url_is_local($url)) || (strpos($url,get_domain())!==false);
			$given_url=$url;
			if (($url!='') && ($url[0]!='#'))
			{
				/*if (substr($url,0,1)=='/')
				{
					$url_full=preg_replace('#/.*#','',get_base_url()).'/'.$url;
				} else*/
				{
					if (substr($url,0,1)=='/') $url=substr($url,1);
					$url_full=url_is_local($url)?(get_base_url().'/'.$url):$url;
					if ($GLOBALS['XSS_DETECT']) ocp_mark_as_escaped($url_full);
				}
			} else $url_full=$url;
			$striped_base_url=str_replace('www.','',str_replace('http://','',get_base_url()));
			if (($striped_base_url!='') && (substr($url,0,1)!='%') && (strpos($url_full,$striped_base_url)===false)) // We don't want to hammer our own server when we have Comcode pages full of links to our own site (much less risk of hammering other people's servers, as we won't tend to have loads of links to them). Would also create bugs in emails sent out - e.g. auto-running validateip.php links hence voiding the intent of the feature.
			{
				$temp_tpl=test_url($url_full,'url',$given_url,$source_member);
			}

			// Render
			if (!array_key_exists('target',$attributes)) $attributes['target']=$local?'_top':'_blank';
			if ($attributes['target']=='blank') $attributes['target']='_blank';
			$rel=(($as_admin) || has_specific_permission($source_member,'search_engine_links'))?'':'nofollow';
			if ($attributes['target']=='_blank')
			{
				$title=do_lang_tempcode('LINK_NEW_WINDOW');
			} else $title='';
			$temp_tpl->attach(do_template('COMCODE_URL',array('_GUID'=>'d1657530e6d3d57e6a4791fb3bfa0dd7','TITLE'=>$title,'REL'=>$rel,'TARGET'=>$attributes['target'],'URL'=>$url_full,'CAPTION'=>$caption)));
			break;
		case 'email':
			$_embed=$embed->evaluate();
			require_code('type_validation');
			require_code('obfuscate');

			// If we need to switch
			if ((is_object($attributes['param'])) || ((!is_valid_email_address($_embed)) && (is_valid_email_address($attributes['param']))))
			{
				$temp=$embed; // Is tempcode
				$_embed=$attributes['param'];
				$attributes['param']=$temp;
			} else
			{
				$attributes['param']=comcode_to_tempcode($attributes['param'],$source_member,$as_admin,60,NULL,$connection,false,false,false,false,false,$highlight_bits,$on_behalf_of_member); // Becomes tempcode
			}
			if ($attributes['param']->is_empty()) $attributes['param']=obfuscate_email_address($_embed);
			$subject=array_key_exists('subject',$attributes)?$attributes['subject']:'';
			$body=array_key_exists('body',$attributes)?$attributes['body']:'';
			$title='';
			if (array_key_exists('title',$attributes)) $title=$attributes['title'];
			$temp_tpl=do_template('COMCODE_EMAIL',array('_GUID'=>'5f6ade8fe07701b6858575153d78f4e9','TITLE'=>$title,'ADDRESS'=>obfuscate_email_address($_embed),'SUBJECT'=>$subject,'BODY'=>$body,'CAPTION'=>$attributes['param']));
			break;
		case 'reference':
			if ($wml)
			{
				break;
			}

			if ((array_key_exists('type',$attributes)) && ($attributes['type']=='url'))
			{
				$_embed=$embed->evaluate();
				check_naughty_javascript_url($source_member,$_embed,$as_admin);
				if (!array_key_exists('title',$attributes)) $attributes['title']=$attributes['param'];
				if ((is_object($attributes['title'])) || ($attributes['title']!=''))
				{
					$_title=is_object($attributes['title'])?$attributes['title']:comcode_to_tempcode($attributes['title'],$source_member,$as_admin,60,NULL,$connection,false,false,false,false,false,$highlight_bits,$on_behalf_of_member);
					$title=$_title->evaluate();
				} else $title=$_embed;
				$embed=hyperlink($_embed,$title,true);
			}
			$temp_tpl=do_template('COMCODE_REFERENCE',array_merge($attributes,array('SOURCE'=>$embed)));
			break;
		case 'upload': // This points to a file path, not a URL
			$_embed=$embed->evaluate();
			$type=(array_key_exists('type',$attributes))?$attributes['type']:'downloads';
			if ((is_object($attributes['param'])) || ($attributes['param']!=''))
			{
				$_caption=is_object($attributes['param'])?$attributes['param']:comcode_to_tempcode($attributes['param'],$source_member,$as_admin,60,NULL,$connection,false,false,false,false,false,$highlight_bits,$on_behalf_of_member);
				$__caption=$_caption->evaluate();
			} else $__caption=$_embed;
			$url=get_custom_base_url().'/'.$type.'/'.rawurlencode($_embed);
			check_naughty_javascript_url($source_member,$url,$as_admin);
			$temp_tpl=test_url($url,'upload',$_embed,$source_member);
			$temp_tpl->attach(hyperlink($url,$__caption));
			break;
		case 'page':
			$ignore_if_hidden=(array_key_exists('ignore_if_hidden',$attributes)) && ($attributes['ignore_if_hidden']=='1');
			unset($attributes['ignore_if_hidden']);

			// Two sets of parameters: simple style and complex style; both are completely incompatible
			$hash='';
			if ($attributes==array('param'=>'')) // very simple
			{
				$zone='_SEARCH';
				$caption=$embed;
				$attributes=array('page'=>$caption->evaluate());
			}
			elseif (array_keys($attributes)==array('param')) // simple
			{
				$caption=$embed;

				if ($wml)
				{
					$temp_tpl=$embed;
					break;
				} else
				{
					if (strpos($attributes['param'],':')!==false)
					{
						global $OVERRIDE_SELF_ZONE;
						list($zone,$attributes,$hash)=page_link_decode($attributes['param']);
						if (!array_key_exists('page',$attributes)) $attributes['page']='';
						if (($zone=='_SELF') && (!is_null($OVERRIDE_SELF_ZONE))) $zone=$OVERRIDE_SELF_ZONE;
					} else
					{
						$zone='_SEARCH'; // Changed in v3 from '_SELF', to allow context-sensitivity
						$attributes=array_merge(array('page'=>$attributes['param']),$attributes);
					}
				}
			} else // complex (needs either a caption or additional-page-parameters to trigger
			{
				$caption=(array_key_exists('caption',$attributes))?comcode_to_tempcode($attributes['caption'],$source_member,$as_admin,60,NULL,$connection,false,false,false,false,false,$highlight_bits,$on_behalf_of_member):$embed;

				if ($wml)
				{
					$temp_tpl=$caption;
					break;
				} else
				{
					$zone=($param_given)?$attributes['param']:'_SEARCH'; // Changed in v3 from '_SELF', to allow context-sensitivity
					unset($attributes['caption']);
					if (!array_key_exists('page',$attributes)) $attributes=array_merge(array('page'=>$embed->evaluate()),$attributes);
				}
			}
			unset($attributes['param']);
			foreach ($attributes as $key=>$val)
			{
				if (is_object($val)) $attributes[$key]=$val->evaluate();
			}
			if ($zone=='_SEARCH')
			{
				$zone=get_page_zone($attributes['page'],false);
				if (is_null($zone)) $zone='';
			}
			$pl_url=build_url($attributes,$zone,NULL,false,false,false,$hash);
			$temp_tpl=hyperlink($pl_url,$caption);
			$page=$attributes['page'];

			if ($page!='')
			{
				if ($zone=='_SELF') $zone=get_zone_name();
				if ($zone=='_SEARCH')
				{
					$zone=get_page_zone($page,false);
					if (is_null($zone)) $zone=''; // Oh dear, well it will be correctly identified as not found anyway
				}
				$ptest=_request_page($page,$zone);
				if ($ptest!==false)
				{
					if (($page=='topicview') && (array_key_exists('id',$attributes)))
					{
						if (!is_numeric($attributes['id']))
							$attributes['id']=$GLOBALS['SITE_DB']->query_value_null_ok('url_id_monikers','m_resource_id',array('m_resource_page'=>$page,'m_moniker'=>$attributes['id']));
						if (!is_null($attributes['id']))
						{
							$test=$GLOBALS['FORUM_DB']->query_value_null_ok('f_topics','id',array('id'=>$attributes['id']));
							if (is_null($test)) $ptest=false;
						} else $ptest=false;
					}
				}
				if ($ptest===false)
				{
					//$temp_tpl->attach(' ['.do_lang('MISSING_RESOURCE').']');  // Don't want this as we might be making the page immediately
					if ((!in_array(get_page_name(),$GLOBALS['DONT_CARE_MISSING_PAGES'])) && (!running_script('iframe')))
					{
						if ($ignore_if_hidden)
						{
							$temp_tpl=do_template('COMCODE_DEL',array('CONTENT'=>$caption));
						} else
						{
							require_code('failure');
							relay_error_notification(do_lang('MISSING_RESOURCE_COMCODE','page',$zone.':'.$page),false,$GLOBALS['FORUM_DRIVER']->is_staff($source_member)?'error_occurred_missing_reference_important':'error_occurred_missing_reference');
						}
					}
				}
			}
			break;
		case 'hide':
			if ($wml)
			{
				$temp_tpl=$embed;
				break;
			}

			if (array_key_exists('param',$attributes))
			{
				$text=is_object($attributes['param'])?$attributes['param']:comcode_to_tempcode($attributes['param'],$source_member,$as_admin,60,NULL,$connection,false,false,false,false,false,$highlight_bits,$on_behalf_of_member);
			} else
			{
				$text=do_lang_tempcode('EXPAND');
			}
			$temp_tpl=do_template('COMCODE_HIDE',array('_GUID'=>'a591a0d1e6bb3dde0f22cebb9c7ab93e','TEXT'=>$text,'CONTENT'=>$embed));
			break;
		case 'quote':
			if ($wml)
			{
				$temp_tpl->attach('<br /><br />'.$attributes['param'].':');
				$temp_tpl->attach($embed);
				break;
			}

			$cite=array_key_exists('cite',$attributes)?$attributes['cite']:NULL;
			if (!is_null($cite))
			{
				$temp_tpl=test_url($cite,'del',$cite,$source_member);
			}

			if ($attributes['param']!='')
			{
				if (is_numeric($attributes['param']))
				{
					$attributes['param']=$GLOBALS['FORUM_DRIVER']->get_username($attributes['param']);
					if (is_null($attributes['param'])) $attributes['param']=do_lang('UNKNOWN');
				} else
				{
					$attributes['param']=protect_from_escaping(comcode_to_tempcode($attributes['param'],$source_member,$as_admin,60,NULL,$connection,false,false,false,false,false,$highlight_bits,$on_behalf_of_member));
				}
				$temp_tpl->attach(do_template('COMCODE_QUOTE_BY',array('_GUID'=>'18f55a548892ad08b0b50b3b586b5b95','CITE'=>$cite,'CONTENT'=>$embed,'BY'=>$attributes['param'],'SAIDLESS'=>array_key_exists('saidless',$attributes)?$attributes['saidless']:'0')));
			} else
			{
				$temp_tpl->attach(do_template('COMCODE_QUOTE',array('_GUID'=>'fa275de59433c17da19b22814c17fdc5','CITE'=>$cite,'CONTENT'=>$embed)));
			}
			break;
		case 'html':
			if ($wml)
			{
				break;
			}

			$temp_tpl=$embed; // Plain HTML. But it's been filtered already
			break;
		case 'semihtml':
			$temp_tpl=$embed; // Hybrid HTML. But it's been filtered already
			break;
		case 'block':
			if ($wml)
			{
				break;
			}

			$attributes['block']=trim($embed->evaluate());
			if (preg_match('#^[\w\-]*$#',$attributes['block'])==0)
			{
				$temp_tpl=paragraph(do_lang_tempcode('MISSING_BLOCK_FILE',escape_html($attributes['block'])),'90dfdlksds8d7dyddssddxs','error_marker');
				break; // Avoids a suspected hack attempt by just filtering early
			}
			$_attributes=array();
			foreach ($attributes as $key=>$val)
			{
				$_attributes[]=$key.'='.$val;
			}
			$temp_tpl=symbol_tempcode('BLOCK',$_attributes);

			break;
		case 'contents':
			if ($wml)
			{
				break;
			}

			// Do structure sweep
			$urls_for=array();

			$old_structure_list=$STRUCTURE_LIST;
			$STRUCTURE_LIST=array(); // reset for e.g. comcode_text_to_tempcode calls (which don't itself reset it, although _comcode_to_tempcode does for top level parses)

			if ((array_key_exists('files',$attributes)) && ($comcode_dangerous))
			{
				$s_zone=array_key_exists('zone',$attributes)?$attributes['zone']:get_zone_name();

				$pages=find_all_pages($s_zone,'comcode_custom/'.get_site_default_lang(),'txt')+find_all_pages($s_zone,'comcode/'.get_site_default_lang(),'txt');
				$prefix=$attributes['files'];
				foreach ($pages as $pg_name=>$pg_type)
				{
					if (substr($pg_name,0,strlen($prefix))==$prefix)
					{
						$i=count($STRUCTURE_LIST);
						comcode_to_tempcode(file_get_contents(zone_black_magic_filterer(get_file_base().'/'.$s_zone.'/pages/'.$pg_type.'/'.$pg_name.'.txt'),FILE_TEXT),$source_member,$as_admin,60,NULL,$connection,false,false,false,true,false,NULL,$on_behalf_of_member);
						$page_url=build_url(array('page'=>$pg_name),$s_zone);
						while (array_key_exists($i,$STRUCTURE_LIST))
						{
							$urls_for[]=$page_url;
							$i++;
						}
					}
				}

				$base=array_key_exists('base',$attributes)?intval($attributes['base']):1;
			} else
			{
				if (substr($comcode,0,8)=='<comcode')
				{
					require_code('comcode_xml');

					if (!$as_admin) check_specific_permission('comcode_dangerous',NULL,$source_member);
					$_=new comcode_xml_to_tempcode($comcode,$source_member,60,NULL,$connection,false,false,false,true,false,$on_behalf_of_member);
				} else
				{
					require_code('comcode_text');

					comcode_text_to_tempcode($comcode,$source_member,$as_admin,60,NULL,$connection,false,false,false,true,false,NULL,$on_behalf_of_member);
				}

				$base=array_key_exists('base',$attributes)?intval($attributes['base']):2;
			}

			$list_types=($embed->evaluate()=='')?array():explode(',',$embed->evaluate());
			$list_types+=array('decimal','lower-alpha','lower-roman','upper-alpha','upper-roman','disc');

			$levels_allowed=array_key_exists('levels',$attributes)?intval($attributes['levels']):NULL;

			// Convert the list structure into a tree structure
			$past_level_stack=array(1);
			$subtree_stack=array(array());
			$levels=1;
			foreach ($STRUCTURE_LIST as $i=>$struct) // Really complex stack of trees algorithm
			{
				$level=$struct[0];
				$title=$struct[1];
				$uniq_id=$struct[2];
				$url=array_key_exists($i,$urls_for)?$urls_for[$i]:'';

				if (($level>$levels_allowed) && (!is_null($levels_allowed))) continue;

				// Going down the tree
				if ($level>$past_level_stack[$levels-1])
				{
					array_push($past_level_stack,$level);
					array_push($subtree_stack,array(array($uniq_id,$title->evaluate(),$url)));
					$levels++;
				} else
				{
					// Going back up the tree, destroying levels that must have now closed off
					while (($level<$past_level_stack[$levels-1]) && ($levels>2/* counting starts at 1, and level 2 is the level at which we cannot jump up a parent level because level 1 is semantically undefined */))
					{
						array_pop($past_level_stack);
						$subtree=array_pop($subtree_stack);
						$levels--;

						// Alter the last of the next level on stack so it is actually taking the closed off level as children, and changing from a property list to a pair: property list & children
						$subtree_stack[$levels-1][count($subtree_stack[$levels-1])-1]=array($subtree_stack[$levels-1][count($subtree_stack[$levels-1])-1],$subtree);
					}

					// Store the title where we are
					$subtree_stack[$levels-1][]=array($uniq_id,$title->evaluate(),$url);
				}
			}

			// Clean up... going up until we're with 1
			while ($levels>1)
			{
				array_pop($past_level_stack);
				$subtree=array_pop($subtree_stack);
				$levels--;
				$parent_level_start_index=count($subtree_stack[$levels-1])-1;
				if ($parent_level_start_index<0)
				{
					$subtree_stack[$levels-1]=$subtree;
				} else
				{
					$subtree_stack[$levels-1][$parent_level_start_index]=array($subtree_stack[$levels-1][$parent_level_start_index],$subtree);
				}
			}

			// Now we have the structure to display
			$levels_t=_do_contents_level($subtree_stack[0],$list_types,$base-1);

			$temp_tpl=do_template('COMCODE_CONTENTS',array('_GUID'=>'ca2f5320fa930e2257a2e74e4f98e5a0','LEVELS'=>$levels_t));

			$STRUCTURE_LIST=$old_structure_list; // Restore, so subsequent 'title' tags have correct numbering

			break;
	}

	// Last ditch effort: custom tags
	if (($temp_tpl->is_definitely_empty()) && (!$wml))
	{
		global $REPLACE_TARGETS;
		if (array_key_exists($tag,$REPLACE_TARGETS))
		{
			$replace=$REPLACE_TARGETS[$tag]['replace'];
			$parameters=explode(',',$REPLACE_TARGETS[$tag]['parameters']);
			$binding=array('CONTENT'=>$embed,'RAND'=>uniqid(''));
			foreach ($parameters as $parameter)
			{
				$parameter=trim($parameter);
				$parts=explode('=',$parameter);
				if (count($parts)==1) $parts[]='';
				if (count($parts)!=2) continue;
				list($parameter,$default)=$parts;
				if ((!array_key_exists($parameter,$attributes)) || ($attributes[$parameter]=='')) $attributes[$parameter]=$default;
				$binding[strtoupper($parameter)]=$attributes[$parameter];
				$replace=str_replace('{'.$parameter.'}','{'.strtoupper($parameter).'*}',$replace);
			}
			$replace=str_replace('{content}',array_key_exists($tag,$GLOBALS['TEXTUAL_TAGS'])?'{CONTENT}':'{CONTENT*}',$replace);
			require_code('tempcode_compiler');
			$temp_tpl=template_to_tempcode($replace);
			$temp_tpl=$temp_tpl->bind($binding,'(custom comcode: '.$tag.')');
		}
	}

	return $temp_tpl;
}

/**
 * Recursive algorithm to make table of contents.
 *
 * @param  array			The TOC (sub)tree
 * @param  array			The list types to use for each level
 * @param  integer		The level to start from
 * @param  integer		The level we are at in the recursion
 * @return tempcode		The TOC node.
 */
function _do_contents_level($tree_structure,$list_types,$base,$the_level=0)
{
	$lines=new ocp_tempcode();
	foreach ($tree_structure as $level)
	{
		if ((is_array($level)) && (!is_string($level[0]))) // A pair: title array and subtree
		{
			$_line=do_template('COMCODE_CONTENTS_LINE_FINAL',array('_GUID'=>'a3dd1bf2e16080993cf72edccb7f3608','URL'=>$level[0][2],'LINE'=>$level[0][1],'ID'=>$level[0][0]));
			$under=_do_contents_level($level[1],$list_types,$base,$the_level+1);
			if ($the_level+1==$base-1) return $under; // Top level not assembled because it has top level title, above contents
			$_line->attach($under);
		} else
		{
			$_line=do_template('COMCODE_CONTENTS_LINE_FINAL',array('_GUID'=>'b675a34f6a950d0e27f163b4b9227b05','URL'=>$level[2],'LINE'=>$level[1],'ID'=>$level[0]));
		}

		$lines->attach(do_template('COMCODE_CONTENTS_LINE',array('_GUID'=>'f6891cb85d93facbc37f7fd3ef403950','LINE'=>$_line)));
	}

	return do_template('COMCODE_CONTENTS_LEVEL',array('_GUID'=>'cd2811bf69387ca05bf9612319db956b','TYPE'=>$list_types[max($the_level-$base,0)],'LINES'=>$lines));
}

/**
 * Turn keys of a map to upper case, and return modified map.
 *
 * @param  array			Input map
 * @return array			Adjusted map
 */
function map_keys_to_upper($array)
{
	$out=array();
	foreach ($array as $key=>$val)
	{
		$out[strtoupper($key)]=$val;
	}
	return $out;
}


