$Date: 2006-10-04 11:22:28 +0100 (Wed, 04 Oct 2006) $ | > $Revision: 605 $ | > $Author: matt $ +--------------------------------------------------------------------------- | | > Posting RTE Editor | > Module written by Matt Mecham | > Date started: Thursday 10th March 2005 13:47 | +-------------------------------------------------------------------------- */ /** * Text Editor: RTE (WYSIWYG) Class * * Class for parsing RTE * * @package InvisionPowerBoard * @subpackage TextEditor * @author Matt Mecham * @version 2.1 * @since 2.1.0 */ /** * */ /** * Text Editor: RTE (WYSIWYG) Class * * Class for parsing RTE * * @package InvisionPowerBoard * @subpackage TextEditor * @author Matt Mecham * @version 2.1 * @since 2.1.0 */ if ( ! defined( 'IN_IPB' ) ) { print "
' );
}
//-----------------------------------------
// WYSI-Weirdness #1: Named anchors
//-----------------------------------------
$t = preg_replace( "#(.+?)#is", "\\1", $t );
//-----------------------------------------
// WYSI-Weirdness #1.5: Empty a hrefs
//-----------------------------------------
$t = preg_replace( "#]+)>#is" , "" , $t );
$t = preg_replace( "#\\1(.+?)#is", "\\1", $t );
//-----------------------------------------
// WYSI-Weirdness #1.6: Double linked links
//-----------------------------------------
$t = preg_replace( "#href=[\"']\w+://(%27|'|\"|")(.+?)\\1[\"']#is", "href=\"\\2\"", $t );
//-----------------------------------------
// WYSI-Weirdness #2: Headline tags
//-----------------------------------------
$t = preg_replace( "#<(h[0-9])>(.+?)\\1>#is", "\n[b]\\2[/b]\n", $t );
//-----------------------------------------
// WYSI-Weirdness #2.5: Fix up smilies
//-----------------------------------------
# Legacy Fix: Old smilies
$t = str_replace( 'emoid=":""' , 'emoid="{{:"}}"', $t );
$t = str_replace( 'emoid=":-""', 'emoid="{{:-"}}"', $t );
# Make EMO_DIR safe so the ^> regex works
$t = str_replace( "<#EMO_DIR#>", "<#EMO_DIR>", $t );
# Parse...
$t = preg_replace_callback( "#[ ]?<([^>]+?)emoid=\"(.+?)\"([^>]+?)?".">[ ]?#is", array( $this, 'html_emoid' ), $t );
# And convert it back again...
$t = str_replace( "<#EMO_DIR>", "<#EMO_DIR#>", $t );
# Convert back
$t = str_replace( "{{:"}}" , ':"' , $t );
$t = str_replace( "{{:-"}}", ':-"', $t );
//-----------------------------------------
// WYSI-Weirdness #3: Image tags
//-----------------------------------------
$t = preg_replace( "#
";
print nl2br(htmlspecialchars($ot));
print "
";
print nl2br($t);
print "
";
exit();
}
//-----------------------------------------
// Done
//-----------------------------------------
return $t;
}
/*-------------------------------------------------------------------------*/
// Parse list tags
/*-------------------------------------------------------------------------*/
/**
* RTE: Parse List tag
*
* @param string Tag
* @param string Text between opening and closing tag
* @param string Opening tag complete
* @param string Parse tag
* @return string Converted text
*/
function _parse_list_tag( $tag, $between_text, $opening_tag, $parse_tag )
{
$list_type = trim( preg_replace( '#"?list-style-type:\s+?([\d\w\_\-]+);?"?#si', '\\1', $this->_get_value_of_option( 'style', $opening_tag ) ) );
//-----------------------------------------
// Set up a default...
//-----------------------------------------
if ( ! $list_type and $tag == 'ol' )
{
$list_type = 'decimal';
}
//-----------------------------------------
// Tricky regex to clean all list items
//-----------------------------------------
$between_text = preg_replace('#
Start tags: {$this->start_tags}
End tags: {$this->end_tags}
Between text:
".htmlspecialchars($between_text)."
";
}
//-----------------------------------------
// Check for inline style moz may have added
//-----------------------------------------
$this->_parse_style_attributes( $opening_tag, $start_tags, $end_tags );
//-----------------------------------------
// Now parse align (if any)
//-----------------------------------------
$align = $this->_get_value_of_option( 'align', $opening_tag );
if ( $align == 'center' )
{
$start_tags .= '[center]';
$end_tags .= '[/center]';
}
else if ( $align == 'left' )
{
$start_tags .= '[left]';
$end_tags .= '[/left]';
}
else if ( $align == 'right' )
{
$start_tags .= '[right]';
$end_tags .= '[/right]';
}
//$end_tags .= "\n";
//-----------------------------------------
// Get recursive text
//-----------------------------------------
$final = $this->_recurse_and_parse( 'div', $between_text, '_parse_div_tag' );
//-----------------------------------------
// #DEBUG
//-----------------------------------------
if ( $this->debug == 2 )
{
print "\n
FINISHED
".$start_tags . $final . $end_tags."
";
}
//-----------------------------------------
// Now return
//-----------------------------------------
return $start_tags . $final . $end_tags;
}
/*-------------------------------------------------------------------------*/
// Parse style attributes
/*-------------------------------------------------------------------------*/
/**
* RTE: Parse style attributes (color, font, size, b, i..etc)
*
* @param string Opening tag complete
* @return string Converted text
*/
function _parse_style_attributes( $opening_tag, &$start_tags, &$end_tags )
{
$style_list = array(
array('tag' => 'color' , 'rx' => '(? 1),
array('tag' => 'font' , 'rx' => 'font-family:\s*([^;]+);?' , 'match' => 1),
array('tag' => 'size' , 'rx' => 'font-size:\s*([\d]+);?' , 'match' => 1),
array('tag' => 'b' , 'rx' => 'font-weight:\s*(bold);?'),
array('tag' => 'i' , 'rx' => 'font-style:\s*(italic);?'),
array('tag' => 'u' , 'rx' => 'text-decoration:\s*(underline);?'),
array('tag' => 'left' , 'rx' => 'text-align:\s*(left);?'),
array('tag' => 'center', 'rx' => 'text-align:\s*(center);?'),
array('tag' => 'right' , 'rx' => 'text-align:\s*(right);?'),
);
//-----------------------------------------
// get style option
//-----------------------------------------
$style = $this->_get_value_of_option( 'style', $opening_tag );
//-----------------------------------------
// Convert RGB to hex
//-----------------------------------------
$style = preg_replace_callback( '#(?convert_realsize_to_bbsize($match[$data['match']]) ."]";
}
}
else
{
$start_tags .= "[{$data['tag']}]";
}
$end_tags = "[/{$data['tag']}]" . $end_tags;
}
}
}
/*-------------------------------------------------------------------------*/
// Parse font tags
/*-------------------------------------------------------------------------*/
/**
* RTE: Parse FONT tag
*
* @param string Tag
* @param string Text between opening and closing tag
* @param string Opening tag complete
* @param string Parse tag
* @return string Converted text
*/
function _parse_font_tag( $tag, $between_text, $opening_tag, $parse_tag )
{
$font_tags = array( 'font' => 'face', 'size' => 'size', 'color' => 'color' );
$start_tags = "";
$end_tags = "";
//-----------------------------------------
// Check for attributes
//-----------------------------------------
foreach( $font_tags as $bbcode => $string )
{
$option = $this->_get_value_of_option( $string, $opening_tag );
if ( $option )
{
$start_tags .= "[$bbcode=\"$option\"]";
$end_tags = "[/$bbcode]" . $end_tags;
if ( $this->debug == 2 )
{
print "
Got bbcode=$bbcode / opening_tag=$opening_tag";
print "
- Adding [$bbcode=\"$option\"] [/$bbcode]";
print "
-- start tags now: {$start_tags}";
print "
-- end tags now: {$end_tags}";
}
}
}
//-----------------------------------------
// Now check for inline style moz may have
// added
//-----------------------------------------
$this->_parse_style_attributes( $opening_tag, $start_tags, $end_tags );
return $start_tags . $this->_recurse_and_parse( 'font', $between_text, '_parse_font_tag' ) . $end_tags;
}
/*-------------------------------------------------------------------------*/
// Parse anchor tags
/*-------------------------------------------------------------------------*/
/**
* RTE: Simple tags
*
* @param string Tag
* @param string Text between opening and closing tag
* @param string Opening tag complete
* @param string Parse tag
* @return string Converted text
*/
function _parse_simple_tag( $tag, $between_text, $opening_tag, $parse_tag )
{
if ( ! $parse_tag )
{
$parse_tag = $tag;
}
return "[$parse_tag]".$this->_recurse_and_parse( $tag, $between_text, '_parse_simple_tag', $parse_tag )."[/$parse_tag]";
}
/*-------------------------------------------------------------------------*/
// Parse simple tag
/*-------------------------------------------------------------------------*/
/**
* RTE: Parse A HREF tag
*
* @param string Tag
* @param string Text between opening and closing tag
* @param string Opening tag complete
* @param string Parse tag
* @return string Converted text
*/
function _parse_anchor_tag( $tag, $between_text, $opening_tag, $parse_tag='' )
{
$mytag = 'url';
$href = $this->_get_value_of_option( 'href', $opening_tag );
$href = str_replace( '<', '<', $href );
$href = str_replace( '>', '>', $href );
$href = str_replace( ' ', '%20' , $href );
if ( preg_match( '#^mailto\:#is', $href ) )
{
$mytag = 'email';
$href = str_replace( "mailto:", "", $href );
}
return "[$mytag=\"$href\"]".$this->_recurse_and_parse( $tag, $between_text, '_parse_anchor_tag', $parse_tag )."[/$mytag]";
}
/*-------------------------------------------------------------------------*/
// Recursively parse tags
/*-------------------------------------------------------------------------*/
/**
* RTE: Recursively parse tags
*
* @param string Tag
* @param string Text between opening and closing tag
* @param string Callback Function
* @param string Parse tag
* @return string Converted text
*/
function _recurse_and_parse( $tag, $text, $function, $parse_tag='' )
{
//-----------------------------------------
// INIT
//-----------------------------------------
$tag = strtolower($tag);
$open_tag = "<".$tag;
$open_tag_len = strlen($open_tag);
$close_tag = "".$tag.">";
$close_tag_len = strlen($close_tag);
$start_search_pos = 0;
$tag_begin_loc = 1;
//-----------------------------------------
// Start the loop
//-----------------------------------------
while ( $tag_begin_loc !== FALSE )
{
$lowtext = strtolower($text);
$tag_begin_loc = @strpos($lowtext, $open_tag, $start_search_pos);
$lentext = strlen($text);
$quoted = '';
$got = FALSE;
$tag_end_loc = FALSE;
//-----------------------------------------
// No opening tag? Break
//-----------------------------------------
if ( $tag_begin_loc === FALSE )
{
break;
}
//-----------------------------------------
// Pick through text looking for delims
//-----------------------------------------
for ( $end_opt = $tag_begin_loc; $end_opt <= $lentext; $end_opt++ )
{
$chr = $text{$end_opt};
//-----------------------------------------
// We're now in a quote
//-----------------------------------------
if ( ( in_array( $chr, $this->delimiters ) ) AND $quoted == '' )
{
$quoted = $chr;
}
//-----------------------------------------
// We're not in a quote any more
//-----------------------------------------
else if ( ( in_array( $chr, $this->delimiters ) ) AND $quoted == $chr )
{
$quoted = '';
}
//-----------------------------------------
// Found the closing bracket of the open tag
//-----------------------------------------
else if ( $chr == '>' AND ! $quoted )
{
$got = TRUE;
break;
}
else if ( ( in_array( $chr, $this->non_delimiters ) ) AND ! $tag_end_loc )
{
$tag_end_loc = $end_opt;
}
}
//-----------------------------------------
// Not got the complete tag?
//-----------------------------------------
if ( ! $got )
{
break;
}
//-----------------------------------------
// Not got a tag end location?
//-----------------------------------------
if ( ! $tag_end_loc )
{
$tag_end_loc = $end_opt;
}
//-----------------------------------------
// Extract tag options...
//-----------------------------------------
$tag_opts = substr( $text , $tag_begin_loc + $open_tag_len, $end_opt - ($tag_begin_loc + $open_tag_len) );
$actual_tag_name = substr( $lowtext, $tag_begin_loc + 1 , ( $tag_end_loc - $tag_begin_loc ) - 1 );
//-----------------------------------------
// Check against actual tag name...
//-----------------------------------------
if ( $actual_tag_name != $tag )
{
$start_search_pos = $end_opt;
continue;
}
//-----------------------------------------
// Now find the end tag location
//-----------------------------------------
$tag_end_loc = strpos( $lowtext, $close_tag, $end_opt );
//-----------------------------------------
// Not got one? Break!
//-----------------------------------------
if ( $tag_end_loc === FALSE )
{
break;
}
//-----------------------------------------
// Check for nested tags
//-----------------------------------------
$nest_open_pos = strpos($lowtext, $open_tag, $end_opt);
while ( $nest_open_pos !== FALSE AND $tag_end_loc !== FALSE )
{
//-----------------------------------------
// It's not actually nested
//-----------------------------------------
if ( $nest_open_pos > $tag_end_loc )
{
break;
}
if ( $this->debug == 2)
{
print "\n\n
( ".htmlspecialchars($open_tag)." ) NEST FOUND\n\n";
}
$tag_end_loc = strpos($lowtext, $close_tag, $tag_end_loc + $close_tag_len);
$nest_open_pos = strpos($lowtext, $open_tag , $nest_open_pos + $open_tag_len );
}
//-----------------------------------------
// Make sure we have an end location
//-----------------------------------------
if ( $tag_end_loc === FALSE )
{
$start_search_pos = $end_opt;
continue;
}
$this_text_begin = $end_opt + 1;
$between_text = substr($text, $this_text_begin, $tag_end_loc - $this_text_begin);
$offset = $tag_end_loc + $close_tag_len - $tag_begin_loc;
//-----------------------------------------
// Pass to function
//-----------------------------------------
$final_text = $this->$function($tag, $between_text, $tag_opts, $parse_tag);
//-----------------------------------------
// #DEBUG
//-----------------------------------------
if ( $this->debug == 2)
{
print "
REPLACED {$function}($tag, ..., $tag_opts):
".htmlspecialchars(substr($text, $tag_begin_loc, $offset))."
WITH:
".htmlspecialchars($final_text)."
NEXT ITERATION";
}
//-----------------------------------------
// Swap text
//-----------------------------------------
$text = substr_replace($text, $final_text, $tag_begin_loc, $offset);
$start_search_pos = $tag_begin_loc + strlen($final_text);
}
return $text;
}
/**
* RTE: parse recursively: Quick method
*
* @param string Tag
* @param string Text between opening and closing tag
* @param string Callback Function
* @param string Parse tag
* @return string Converted text
*/
function _recurse_and_parse_quick( $tag, $text, $function, $parse_tag='' )
{
//-----------------------------------------
// Set up
//-----------------------------------------
$tag = strtolower( $tag );
$open_tag = "<".$tag;
$close_tag = "".$tag.">";
$lowtext = strtolower( $text );
//-----------------------------------------
// Return if no content or no opening tag
//-----------------------------------------
if ( ( ! $text ) OR ( ! strstr( $lowtext, $open_tag ) ) )
{
return $text;
}
//-----------------------------------------
// Go loopy
//-----------------------------------------
while( strstr( $lowtext, $open_tag ) !== FALSE )
{
//-----------------------------------------
// Quick check
//-----------------------------------------
if ( ! strstr( $lowtext, $open_tag ) )
{
break;
}
if ( ! strstr( $lowtext, $close_tag ) )
{
break;
}
//-----------------------------------------
// Try and get a match. First tag to the
// very last tag
//-----------------------------------------
preg_match( "#($open_tag(?:.+?)?".">)(.+?)$close_tag#is", $text, $match );
if ( $this->debug == 2 )
{
print "";
print_r( array_map('htmlspecialchars',$match) );
print "";
}
$entire_match = $match[0];
$opening_tag = $match[1];
$between_text = $match[2];
if ( ! $entire_match )
{
break;
}
//-----------------------------------------
// Look for nested tags
//-----------------------------------------
if ( strstr( strtolower( $between_text ), $open_tag ) !== FALSE )
{
if ( $this->debug == 2)
{
print "\n\n
( ".htmlspecialchars($open_tag)." ) NEST FOUND\n\n";
}
//-----------------------------------------
// How many opening tags?
//-----------------------------------------
$open_tag_count = substr_count( $between_text, $open_tag );
$close_tag_count = substr_count( $between_text, $close_tag );
$chopped_text = $entire_match;
$tmp_text = preg_replace( "#(^|.+?)".preg_quote( $entire_match, '#' )."#s", '', $text );
//-----------------------------------------
// Okay, pick through the text finding as
// many closing tags as we have opening tags
//-----------------------------------------
while( ( $open_tag_count != $close_tag_count ) OR $tmp_text != '' )
{
preg_match( "#(.+?)$close_tag#is", $tmp_text, $match );
if ( $this->debug == 2)
{
print "
IN RECURSE TMP TEXT: ".htmlspecialchars($tmp_text)."
Open tags: {$open_tag_count}
Closed tags: {$close_tag_count}
";
}
if ( $match[1] )
{
$open_tag_count += intval( substr_count( $match[1], $open_tag ) );
$close_tag_count++;
$chopped_text .= $match[0];
$tmp_text = preg_replace( "#(^|.+?)".preg_quote( $match[0], '#' )."#s", '', $tmp_text );
}
else
{
// Cannot find, somethings wrong so break
break;
}
}
//-----------------------------------------
// Piece back together the 'between_text'
//-----------------------------------------
preg_match( "#$open_tag(?:.+?)?".">(.*)$close_tag#is", $chopped_text, $newmatch );
$between_text = $newmatch[1];
$entire_match = $chopped_text;
}
//-----------------------------------------
// Pass to handler
//-----------------------------------------
if ( $between_text )
{
$newtext = $this->$function( $tag, $between_text, $opening_tag, $parse_tag );
//$text = preg_replace( "#".preg_quote( $entire_match, '#' )."#", $newtext, $text );
$text = str_replace( $entire_match, $newtext, $text );
if ( $this->debug == 2)
{
print "
REPLACED:
".htmlspecialchars($entire_match)."
WITH:
".htmlspecialchars($newtext)."
TEXT IS NOW:
".htmlspecialchars($text)."
";
}
}
}
if ( $this->debug == 2)
{
print "
NEW TEXT: ".htmlspecialchars($text)."\n\n
ALL DONE - RETURNING \$text now....
";
}
return $text;
}
/*-------------------------------------------------------------------------*/
// Get value of option
/*-------------------------------------------------------------------------*/
/**
* RTE: Extract option HTML
*
* @param string Option
* @param string Text
* @return string Converted text
*/
function _get_value_of_option( $option, $text )
{
if( $option == 'face' )
{
// Bad font face, bad
preg_match( "#$option(\s+?)?\=(\s+?)?[\"']?(.+?)([\"']|$|color|size|>)#is", $text, $matches );
}
else
{
preg_match( "#$option(\s+?)?\=(\s+?)?[\"']?(.+?)([\"']|(\s+?)|$|>)#is", $text, $matches );
}
//preg_match( "#$option(\s+?)?\=(\s+?)?[\"']?(.+?)([\"']|$|>|\s)#is", $text, $matches );
return isset($matches[3]) ? trim( $matches[3] ) : '';
}
}
?>