<?php /** @class o7aCode * * @author Bertrand Brasselet <cntc.1@o7acode.net> * @version 1.1.3 * @since 26/02/2007 * http://o7acode.net/ * */ class o7aCode { /** * * @var array param contain all parameters of the class ; detailed below. * @access public * * ---- * * @var boolean param['active_*something*'] do the syntax for *something* must be translated ? * * = Some clarifications = * ** 'active_link_wikipedia' & 'active_link_lastfm' correspond to extensions of the links syntax ** and allow to more quickly make links to wikipedia/last.fm acticles. ** ** 'active_uppercase' : a letter preceded by a caret ^ is automatically put in uppercase. This ** syntax is useful with accented letters. ** ** 'active_backslash' : a character preceded by a blackslash isn't interpreted. ** ** 'active_blockcode' : display of code in block, within pre markups. * * ---- * * @var boolean param['blockcode_with_color'] do the code in block is colored up (need for GeSHi) ? (1) * @var array param['blockcode_languages_with_css'] list of languages using css class for color up the code (2) * @var boolean param['o7aCode_by_default'] do the document is written by default in o7aCode ? (3) * @var boolean param['insert_xhtml'] do xhtml parts can be incorporated within o7aCode ? (4) * @var boolean param['convert_by_htmlentities'] do the content must be convert by htmlentities ? (5) * @var string param['str_encoding'] fill in the characters encoding (used by htmlentities) * @var integer param['first_title_level'] xhtml level of the o7aCode first level titles * * (1) & (2) For more info about GeSHi and the CSS associated, look at the end of the file. * (2) Other languages are colored up puting css within the style attribute of the span markups. * (3) If false, o7aCode parts must be between <## and ##>. * (4) Xhtml parts must be between #> and <#. * (5) The activation of this option is highly recommended ! * * = Note = * ** If param['o7aCode_by_default'] is set to false, param['insert_xhtml'] is in either case ** considered as true. * * ---- * * @var string param['link_wikipedia_lang'] fill in the language of the wikipedia links (en,fr...) * @var string param['link_wikipedia_title'] give the title attribute of the wikipedia links (*) * @var string param['link_lastfm_lang'] fill in the language of the lastfm links (en,fr...) * @var string param['link_lastfm_title'] give the title attribute of the lastfm links (*) * * (*) These values are followed by the article name. * */ var $param; /** * * @var array[string] backslash each characters deactived by backslashes (*) * @access private * @var array[string] xhtml_blended each xhtml parts inserted within o7aCode (*) * @access private * @var array[string] block_code each code in block to display, within pre markups (*) * @access private * * (*) all kept aside during the interpreting then put back on its initial positions. * */ var $backslash; var $xhtml_blended; var $block_code; /** * @method o7aCode initialize all parameters * @since 1.0.0 * @access public */ function o7aCode() { $this->setParam('active_comment',true); $this->setParam('active_title',true); $this->setParam('active_paragraph',true); $this->setParam('active_list',true); $this->setParam('active_link',true); $this->setParam('active_link_wikipedia',true); $this->setParam('active_link_lastfm',true); $this->setParam('active_img',true); $this->setParam('active_strongEmphasis',true); $this->setParam('active_emphasis',true); $this->setParam('active_del',true); $this->setParam('active_ins',true); $this->setParam('active_quote',true); $this->setParam('active_cite',true); $this->setParam('active_blockquote',true); $this->setParam('active_code',true); $this->setParam('active_abbr',true); $this->setParam('active_hr',true); $this->setParam('active_br',true); $this->setParam('active_EnDash',true); $this->setParam('active_clearer',true); $this->setParam('active_uppercase',true); $this->setParam('active_backslash',true); $this->setParam('active_blockcode',true); $this->setParam('blockcode_with_color',true); $this->setParam('blockcode_languages_with_css',array('xml','php','css')); $this->setParam('insert_xhtml',true); $this->setParam('convert_by_htmlentities',true); $this->setParam('str_encoding','ISO-8859-1'); $this->setParam('first_title_level',3); $this->setParam('o7aCode_by_default',true); $this->setParam('link_wikipedia_lang','fr'); $this->setParam('link_wikipedia_title','Article de Wikipedia sur '); $this->setParam('link_lastfm_lang','fr'); $this->setParam('link_lastfm_title','Fiche de Last.fm sur le groupe '); } /** * @metod setParam set a parameter value * @param string parameter parameter entitled * @param string value parameter value * @since 1.0.0 * @access public */ function setParam($parameter, $value) { $this->param[$parameter] = $value; } /** * @metod getParam gives the value of a specified parameter * @param string parameter parameter entitled * @return string parameter value * @since 1.0.0 * @access protected */ function getParam($parameter) { return (!empty($this->param[$parameter])) ? $this->param[$parameter] : false; } /** * @metod transform Final method, give parse() only the o7aCode parts * @param string content xhtml / o7aCode document * @return string final xhtml document * @since 1.0.0 * @access public * */ function transform($content) { /// Interpretation of o7aCode parts which are between <## and ##> if (!$this->getParam('o7aCode_by_default')) { $regex = '/<##(.*)##>/Umse'; # don't quiver ! All these backslashes allow to not modify the quote characters of our '\1' $php_action = get_class($this)."::parse(preg_replace('#\\\\\\\\\\\\\"#','\"','\\1'))"; $final_xhtml = preg_replace($regex,$php_action,$content); } /// The document must only be in o7aCode. else { # All the document is interpreted $final_xhtml = $this->parse($content); } return $final_xhtml; } /** * @metod parse Convert o7aCode into xhtml * @param string o7aCode an o7aCode part * @return string the converted (xhtml) version of the part * @since 1.0.0 * @access private * * = Legend = * ** '## ITEM' precede this item interpretation. ** ** In this method, some item interpretations are splitted into two parts, the PING one ** where some parts of the document are kept aside and temporary replaced by recognizable ** strings which, contrary to the parts kept aside, don't bother the following items ** interpretation and aren't modified by them. Secondly, the PONG, these strings are ** replaced by its respective parts. ** ** '==>' corresponds to 'must precede', '::' corresponds to 'because', '(!)' corresponds ** to 'in the contrary case'. ** ** (:ITEM) is an anchor on clarifications of the item interpretation. * */ function parse($o7aCode) { # degree symbol $deg = '(?:'.chr(176).'|'.chr(194).')'; ## BACKSLASH PING if ($this->getParam('active_backslash')) { $o7aCode = preg_replace_callback('#\\\\(.)#m',array(&$this,'charBackslashed2strNum'),$o7aCode); } /// BACKSLASH PING ==> *ALL BELOW* :: backslashes must be able to deactive all the /// o7aCode syntax. ## BLOCKCODE PING if ($this->getParam('active_blockcode')) { $regex = '/^ # on a new line l.1 (\s*)'.$deg.$deg.$deg.' # open l.1 (.*) # specify the language l.1 \r?\n (.*) # code in block l.2..n \r?\n (\s*)'.$deg.$deg.$deg.' # close l.n+1 (.*) # attributes declaration l.n+1 (\r|$) /Usmx'; $o7aCode = preg_replace_callback($regex,array(&$this,'blockCode2strNum'),$o7aCode); } ## INSERT XHTML PING if ($this->getParam('insert_xhtml') || !$this->getParam('o7aCode_by_default')) { $regex = '/(^\s*|)##>(?sU)(.*)(?-sU)<##(\s*$|)/m'; $o7aCode = preg_replace_callback($regex,array(&$this,'xhtml2strNum'),$o7aCode); } /// BLOCKCODE PING & INSERT XHTML PING ==> COMMENT :: comments whithin code /// mustn't be deleted by COMMENT. /// Syntax for in-line comment is two slash, which is routinely met in URLs. The /// two slash of URLs and the preceding colon are temporary replaced by /// "colon_slash_slash_url" in order to not interpret it as comment (:COMMENT). ## COMMENT if ($this->getParam('active_comment')) { # In-line $o7aCode = preg_replace('#://#m','colon_slash_slash_url',$o7aCode); $o7aCode = preg_replace('# ?//.*$#m','',$o7aCode); $o7aCode = preg_replace('#colon_slash_slash_url#m','://',$o7aCode); # Block $o7aCode = preg_replace('#/\*.*\*/#Usm','',$o7aCode); } /// COMMENT ==> *ALL BELOW* /// About the end of the method, html special characters are converted /// (:HTMLENTITIES) ; Only strings between html markups are given to /// htmlentities(), that's to say the strings between > and <.The possible angle /// backets into these strings would cause misfunctioning of the HTMLENTITIES /// regex. That's why before the insertion of every html markup (which contain /// angle backets), all the angle backets must be converted by its html entities /// (:ANGLE BACKETS). /// And-signs are also converted by HTMLENTITIES, but and-signs of angle backets /// html entities mustn't be converted ; Hence the temporary string /// "temporary_and_sign" which take the place of them and will be replace after /// HTMLENTITIES (:AND SIGN PONG). ## ANGLE BACKETS $o7aCode = preg_replace('#<#m','temporary_and_sign'.'lt;',$o7aCode); $o7aCode = preg_replace('#>#m','temporary_and_sign'.'gt;',$o7aCode); /// ANGLE BACKETS ==> *ALL which give html markups* :: see at ANGLE BACKETS anchor. ## HR if ($this->getParam('active_hr')) { $regex = '#^(\s*)-{4,}(.*)$#m'; $o7aCode = preg_replace_callback($regex,array(&$this,'transformHr'),$o7aCode); } /// HR ==> EN DASH :: (!) hr would be interpreted as two en-dashes. #### IN-LINE #### ## LINK if ($this->getParam('active_link')) { $regex = '#\[([^\]]*)\](.*)\[/\]#mU'; $o7aCode = preg_replace_callback($regex,array(&$this,'transformLink'),$o7aCode); } ## IMG if ($this->getParam('active_img')) { $regex = '#\{([^\}]+)\}#m'; $o7aCode = preg_replace_callback($regex,array(&$this,'transformImg'),$o7aCode); } ## CITE if ($this->getParam('active_cite')) { $lt = 'temporary_and_sign'.'lt;'; $gt = 'temporary_and_sign'.'gt;'; $regex= '#'.$lt.$lt.'(.*)'.$gt.$gt.'#mU'; $o7aCode = preg_replace_callback($regex,array(&$this,'transformCite'),$o7aCode); } ## STRONG EMPHASIS if ($this->getParam('active_strongEmphasis')) { $regex = "#'''(.*)'''#mU"; $o7aCode = preg_replace_callback($regex,array(&$this,'transformStrongEmphasis'),$o7aCode); } /// STRONG EMPHASIS ==> EMPHASIS :: (!) strong emphasis whould be interpreted as /// emphasis with two redundant sigle-quotation marks. ## EMPHASIS if ($this->getParam('active_emphasis')) { $regex = "#''(.*)''#mU"; $o7aCode = preg_replace_callback($regex,array(&$this,'transformEmphasis'),$o7aCode); } ## EN DASH if ($this->getParam('active_EnDash')) { $o7aCode = preg_replace('#---#mU','temporary_and_sign'.'ndash;',$o7aCode); } /// EN DASH ==> DEL :: (!) del whould be interpreted as en-dashes with two /// redundant minus sign. ## DEL if ($this->getParam('active_del')) { $o7aCode = preg_replace_callback('#--(.*)--#mU',array(&$this,'transformDel'),$o7aCode); } ## INS if ($this->getParam('active_ins')) { $regex = '#\+\+(.*)\+\+#mU'; $o7aCode = preg_replace_callback($regex,array(&$this,'transformIns'),$o7aCode); } ## CODE INLINE if ($this->getParam('active_code')) { $regex = '#'.$deg.$deg.'(.*)'.$deg.$deg.'#mU'; $o7aCode = preg_replace_callback($regex,array(&$this,'transformCode'),$o7aCode); } ## UPPERCASE if ($this->getParam('active_uppercase')) { $o7aCode = preg_replace('#\^(.)#em','mb_strtoupper("\1")',$o7aCode); } ## ABBR if ($this->getParam('active_abbr')) { $regex = '#\(([^\)]+)\)(.*)\(/\)#mU'; $o7aCode = preg_replace_callback($regex,array(&$this,'transformAbbr'),$o7aCode); } ## BR if ($this->getParam('active_br')) { $o7aCode = preg_replace('#/-/#m','<br />',$o7aCode); } /// The value of an attribute may be empty ; In this case, the html markup contain /// two double quotation marks, which is interpreted by QUOTE. In order to avoid /// this confusion, (:EMPTY VALUES PING) replace these double quotation marks by /// "two_double_quotation_marks". (:EMPTY VALUES PONG), after QUOTE, make the /// upturned replacement. ## QUOTE if ($this->getParam('active_quote')) { $o7aCode = preg_replace_callback('#""(.*)""#mU',array(&$this,'transformQuote'),$o7aCode); } /// QUOTE ==> EMPTY VALUES PONG :: see at the EMPTY VALUES PONG anchor. ## EMPTY VALUES PONG $o7aCode = preg_replace('#two_double_quotation_marks#m','""',$o7aCode); ################# /// IN-LINE ==> IN-BLOCK & PARAGRAPH :: (!) attributes separators ('::', ';;') of /// IN-LINE would be interpreted as attributes separators of IN-BLOCK or PARAGRAPH. #### IN-BLOCK #### ## TITLE if ($this->getParam('active_title')) { $regex = '#^(\s*)(=+)(.+)\2(\r|$)#m'; $o7aCode = preg_replace_callback($regex,array(&$this,'transformTitle'),$o7aCode); } ## BLOCKQUOTE if ($this->getParam('active_blockquote')) { $regex = '#^(\s*)"""\s*\r?\n(.*)\r?\n(\s*)"""(.*)(\r|$)#Usm'; $o7aCode = preg_replace_callback($regex,array(&$this,'transformBlockquote'),$o7aCode); } ## LIST if ($this->getParam('active_list')) { $o7aCode = $this->transformList($o7aCode); } ## CLEARER if ($this->getParam('active_clearer')) { $regex = '#^(\s*)_{4,}\s*(\r|$)#mU'; $o7aCode = preg_replace($regex,'\1))<div class="clearer"></div>',$o7aCode); } #################### /// HR & IN-BLOCK ==> PARAGRAPH :: (!) titles, lists... would be put whithin p /// markups. See also at the ANTI PARAGRAPH PONG anchor. ## PARAGRAPH if ($this->getParam('active_paragraph')) { $o7aCode = $this->transformParagraph($o7aCode); } /// PARAGRAPH ==> ANTI PARAGRAPH PONG ## ANTI PARAGRAPH PONG if (!$this->getParam('active_paragraph')) { $regex = '#^(\s*)\)\)(\s*)(?:<(/?)(h[0-9]+|ul|ol|li|div|hr)>|xhtml_blended_num)#m'; $o7aCode = preg_replace($regex,'\1\2<\3\4>',$o7aCode); } ## HTMLENTITIES $regex = '#(^|>)([^<]*)(<|$)#ms'; $o7aCode = preg_replace_callback($regex,array(&$this,'transformHtmlentities'),$o7aCode); /// HTMLENTITIES ==> AND SIGN PONG :: see at the AND SIGN PONG anchor. ## AND SIGN PONG $o7aCode = preg_replace('#temporary_and_sign#m','&',$o7aCode); ## BLOCKCODE PONG if ($this->getParam('active_blockcode')) { $regex = '#(?:\)\)|)block_code_([0-9]+)#m'; $o7aCode = preg_replace_callback($regex,array(&$this,'strNum2block_code'),$o7aCode); } ## XHTML INLINE PONG if ($this->getParam('insert_xhtml')) { $regex = '#xhtml_blended_num_([0-9]+)#m'; $o7aCode = preg_replace_callback($regex,array(&$this,'strNum2xhtml'),$o7aCode); } /// BACKSLASH PONG ==> *ALL ABOVE* :: after all interpretations. ## BACKSLASH PONG if ($this->getParam('active_backslash')) { $regex = '#char_bs_num_([0-9]+)#m'; $o7aCode = preg_replace_callback($regex,array(&$this,'strNum2charBackslashed'),$o7aCode); } # $xhtml = $o7aCode_interpreted = $o7aCode; $xhtml = $o7aCode; return $xhtml; } /// Keeping aside some items during the interpretation : Methods called /// *something*2strNum() attribute a unique number to each item, which allows methods /// called srtNum2*something* to get back the item knowing the number. /** * Saves the character in $this->backslash and return a temporary string (with the * corresponding number) which will take the place of the character during interpretation. * * @metod charBackslashed2strNum * @param array[string] regex_array array returned by the regex of BACKSLASH PING * @return string the temporary string * @since 1.1.0 * @access protected */ function charBackslashed2strNum($regex_array) { $i = sizeof($this->backslash); $char = $regex_array[1]; //echo ord($char).' - '; $this->backslash[$i] = $char; return 'char_bs_num_'.$i; } /** * * Sorry, but methods above aren't yet well documented ... I will have again free time * about the april 14 2007 to do this. * */ function blockCode2strNum($regex_array) { $attributes = $this->attributesStr2attributesXhtml($regex_array[5],'',0,'class'); $code = $regex_array[3]; /// In codes in block to display, blackslashes mustn't be interpreted. BACKSLASH /// CANCEL cancel BACKSLASH PING actions in $code. ## BACKSLASH CANCEL if ($this->getParam('active_backslash')) { $regex = '#char_bs_num_([0-9]+)#m'; $code = preg_replace_callback($regex,array(&$this,'strNum2charBackslashed2'),$code); } if ($this->getParam('blockcode_with_color')) { $language = strtolower(trim($regex_array[2])); if (preg_match('#html#',$language)) $language = 'xml'; require_once dirname(__FILE__).'/geshi/geshi.php'; $code2color = new geshi ($code,$language); $languages_with_css = $this->getParam('blockcode_languages_with_css'); if (is_array($languages_with_css)) foreach ($languages_with_css as $language_with_css) { if (preg_match('#'.$language_with_css.'#',$language)) $code2color->enable_classes(); } $code = $code2color->parse_code(); if (preg_match('#<pre([^>]*)class="#',$code)) { $code = preg_replace('#<pre([^>]*)class="#','<pre\1class="code ',$code); } else $code = preg_replace('#<pre#','<pre class="code"',$code); } else $code = '<pre class="code">'.$this->htmlentities($code).'</pre>'; $num = sizeof($this->block_code); $this->block_code[$num] = $code; return '))block_code_'.$num; } function xhtml2strNum($regex_array) { $i = sizeof($this->xhtml_blended); $xhtml = $regex_array[2]; /// In inserted xhtml parts, blackslashes mustn't be interpreted. BACKSLASH CANCEL /// cancel BACKSLASH PING actions in $xhtml. ## BACKSLASH CANCEL if ($this->getParam('active_backslash')) { $regex = '#char_bs_num_([0-9]+)#m'; $xhtml = preg_replace_callback($regex,array(&$this,'strNum2charBackslashed2'),$xhtml); } $this->xhtml_blended[$i] = $xhtml; if ($regex_array[1] && $regex_array[3]) $before = $regex_array[1].'))'; else $before = ''; return $before.'xhtml_blended_num_'.$i; } function transformHr($regex_array) { $attributes = $this->attributesStr2attributesXhtml($regex_array[2],'class'); return $regex_array[1].'))<hr'.$attributes.' />'; } function transformLink($regex_array) { $content = trim($regex_array[2]); if (preg_match('#^\s*(::|;;|$)#',$regex_array[1]) && preg_match('#^[a-zA-Z0-9]{2,6}://#',$content)) { $attributes_str = $content.$regex_array[1]; } else $attributes_str = $regex_array[1]; $attributes = $this->attributesStr2attributesXhtml($attributes_str,'href::title::lang::id'); //### WIKIPEDIA EXTENSION ###// if ($this->GetParam('active_link_wikipedia')) { if (preg_match('#href="(?:wp|wikipedia)(?:\s*:\s*([^"]+)|)"#',$attributes,$word)) { $wikipedia_url = 'http://'.$this->GetParam('link_wikipedia_lang').'.wikipedia.org/wiki/'; if ($word[1] != '') $word = $word[1]; else $word = $content; $attributes = preg_replace('#href="[^"]+"#U','href="'.$wikipedia_url.$word.'"',$attributes); if (!preg_match('#title="#',$attributes)) { $attributes.= ' title="'.$this->GetParam('link_wikipedia_title').'"'.$word.'"'.'"'; } } } //###########################// //### LASTFM EXTENSION ###// if ($this->GetParam('active_link_lastfm')) { if (preg_match('#href="(?:lf|lastfm)(?:\s*:\s*([^"]+)|)"#',$attributes,$word)) { if ($this->GetParam('link_lastfm_lang') == 'en') $lastfm_url = 'http://www.last.fm/music/'; else $lastfm_url = 'http://www.lastfm.'.$this->GetParam('link_lastfm_lang').'/music/'; if ($word[1] != '') $word = $word[1]; else $word = $content; $attributes = preg_replace('#href="[^"]+"#U','href="'.$lastfm_url.$word.'"',$attributes); if (!preg_match('#title="#',$attributes)) { $attributes.= ' title="'.$this->GetParam('link_lastfm_title').$word.'"'; } } } //###########################// return '<a'.$attributes.'>'.$content.'</a>'; } function transformImg($regex_array) { $attributes = $this->attributesStr2attributesXhtml($regex_array[1],'src::alt::class'); return '<img'.$attributes.' />'; } function transformCite($regex_array) { $content_attributes = $this->attributesStr2attributesXhtml($regex_array[1],'class',1); $content = $content_attributes[0]; $attributes = $content_attributes[1]; return '<cite'.$attributes.'>'.$content.'</cite>'; } function transformStrongEmphasis($regex_array) { $content_attributes = $this->attributesStr2attributesXhtml($regex_array[1],'class',1); $content = $content_attributes[0]; $attributes = $content_attributes[1]; return '<strong'.$attributes.'>'.$content.'</strong>'; } function transformEmphasis($regex_array) { $content_attributes = $this->attributesStr2attributesXhtml($regex_array[1],'class',1); $content = $content_attributes[0]; $attributes = $content_attributes[1]; return '<em'.$attributes.'>'.$content.'</em>'; } function transformDel($regex_array) { $content_attributes = $this->attributesStr2attributesXhtml($regex_array[1],'class',1); $content = $content_attributes[0]; $attributes = $content_attributes[1]; return '<del'.$attributes.'>'.$content.'</del>'; } function transformIns($regex_array) { $content_attributes = $this->attributesStr2attributesXhtml($regex_array[1],'class',1); $content = $content_attributes[0]; $attributes = $content_attributes[1]; return '<ins'.$attributes.'>'.$content.'</ins>'; } function transformCode($regex_array) { $content_attributes = $this->attributesStr2attributesXhtml($regex_array[1],'class',1); $content = $content_attributes[0]; $attributes = $content_attributes[1]; return '<code'.$attributes.'>'.$content.'</code>'; } function transformAbbr($regex_array) { $content = trim($regex_array[2]); $attributes = $this->attributesStr2attributesXhtml($regex_array[1],'title::xml:lang'); return '<abbr'.$attributes.'>'.$content.'</abbr>'; } function transformQuote($regex_array) { $content_attributes = $this->attributesStr2attributesXhtml($regex_array[1],'cite::lang::class',1); $content = $content_attributes[0]; $attributes = $content_attributes[1]; return '<q'.$attributes.'>'.$content.'</q>'; } function transformTitle($regex_array) { $level = $this->getParam('first_title_level') + strlen($regex_array[2]) - 1; $entitled = 'h'.$level; $content_attributes = $this->attributesStr2attributesXhtml($regex_array[3],'id',1); $content = $content_attributes[0]; $attributes = $content_attributes[1]; return $regex_array[1].'))<'.$entitled.$attributes.'>'.$content.'</'.$entitled.'>'; } function transformBlockquote($regex_array) { $attributes = $this->attributesStr2attributesXhtml($regex_array[4],'cite::lang::class'); return $regex_array[1].'))'. '<blockquote'.$attributes.'>'."\n".$regex_array[2]."\n".$regex_array[3].'))</blockquote>'; } function transformList($content) { $content_array = preg_split('#\r?\n#m',$content); $content_array[] = false; /** @var array every_lists [](1)* [0](2) [1](3) [](5) [0](6) [1](7)* [2](8) [2](4) (1)* : each distinct lists (<ul> or <ol>) (2) : '*' or '#' for respectively ul or ol (3) : content of the list (4) : attributes of the <ul> or <ol> (5) : each item <li> (6) : content of the item : string (7)* : content of the item : potential array (an under list) (1)* (8) : attributes of the item */ /** @var array list_on_levels (temporary array) [0..n : levels] (6) in editing : (1)* */ for ($i=-1;$i<=sizeof($content_array);$i++) { //########## ACQUISITION ###########// // put in array : $every_lists if (preg_match('#^(\s*)([\*\#]*)([\*\#])(.*)$#',$content_array[$i],$preg_li)) { if ($preg_li[3] == '*') $this_type = 'ul'; else $this_type = 'ol'; $this_level = strlen($preg_li[2]); $this_j = $j; $diff_level = $this_level - $level; if ($diff_level > 0) $this_level = $level+1; $same_level = $diff_level == 0; $same_type = $this_type == $type; if ($diff_level > 0) { $list_on_levels[$this_level] = &$item[1]; $this_j=0; } elseif ($diff_level < 0) { for ($k=0;$k<(-1*$diff_level);$k++) unset($list_on_levels[$level-$k]); $this_j = sizeof($list_on_levels[$this_level]) -1; if ($list_on_levels[$this_level][$this_j][0] != $this_type) $this_j++; } elseif (!$same_type) $this_j++; if ($this_j != $j || !$same_level) $list_on_levels[$this_level][$this_j][0] = $this_type; $item = &$list_on_levels[$this_level][$this_j][1][]; //// ATTRIBUTES / CONTENT ACQUISITION //// $li_content_liul_attributes = preg_split('#\]\]#',$preg_li[4]); $content_attributes = $this->attributesStr2attributesXhtml($li_content_liul_attributes[0],'class',1); $item[0] = $content_attributes[0]; $item[2] = $content_attributes[1]; // UL / OL $ul_attributes = trim($li_content_liul_attributes[1]); if ($ul_attributes != '') { if ($list_on_levels[$this_level][$this_j][2] != '') { $ul_attributes = '::'.$ul_attributes; } $list_on_levels[$this_level][$this_j][2].= $this->attributesStr2attributesXhtml($ul_attributes); } ////////////////////////////////////////// $level = $this_level; $type = $this_type; $j = $this_j; } //##################################// //########## TRANSFORMATION ###########// // array --2--> xhtml else { if (isset($this_j)) { unset($this_j); $xhtml_str_lists = $this->liArray2liXhtml($every_lists); $xhtml_array_lists = preg_split('#\r?\n#m',$xhtml_str_lists); for ($k=0;$k<sizeof($xhtml_array_lists);$k++) { $new_content_array[] = preg_replace('#^(\s*)<#','\1))<',$xhtml_array_lists[$k]); } } unset($every_lists); unset($list_on_levels); unset($type); $every_lists = &$list_on_levels[0]; $j = -1; $level = 0; if (isset($content_array[$i])) $new_content_array[] = $content_array[$i]; } //######################################// } $content = implode("\n",$new_content_array); return $content; } function transformParagraph($content) { $content_array = preg_split('#\r?\n#m',$content); $p_line_nb = 0; for ($i=-1;$i<=sizeof($content_array);$i++) { $is_p = preg_match('#^[^=*+]+#',trim($content_array[$i])); $is_desactivated = preg_match('#^(\s*)\)\)(.*)$#',$content_array[$i],$line); $is_p = $is_p && !$is_desactivated; if ($is_desactivated) $content_array[$i] = $line[1].$line[2]; if ($is_p) $p_line_nb++; if (!$is_p && $p_line_nb > 0) { $p_open_position = $i - $p_line_nb; $p_close_position = $i - 1; $last_p_line_attributes = $this->attributesStr2attributesXhtml($content_array[$p_close_position],'class',1); $last_p_line = $last_p_line_attributes[0]; $attributes = $last_p_line_attributes[1]; if ($p_open_position != $p_close_position) { $first_p_line = $content_array[$p_open_position]; } else $first_p_line = $last_p_line; preg_match('#^(\s*)(.*)$#',$first_p_line,$first_p_line); // <p> beetween if ($p_open_position == $p_close_position) { $line = $first_p_line; $line_position = $p_open_position; $content_array[$line_position] = $line[1].'<p'.$attributes.'>'.$line[2].'</p>'; } else { $content_array[$p_open_position] = $first_p_line[1].'<p'.$attributes.'>'.$first_p_line[2]; $content_array[$p_close_position] = $last_p_line.'</p>'; } $p_line_nb = 0; } } $content = implode("\n",$content_array); return $content; } function transformHtmlentities($regex_array) { return $regex_array[1].$this->htmlentities($regex_array[2]).$regex_array[3]; } function strNum2block_code($num) { $num = $num[1]; return $this->block_code[$num]; } function strNum2xhtml($num) { $num = $num[1]; return $this->xhtml_blended[$num]; } function strNum2charBackslashed($num) { $num = $num[1]; return $this->htmlentities($this->backslash[$num]); } function strNum2charBackslashed2($num) { $num = $num[1]; return '\\'.$this->htmlentities($this->backslash[$num]); } function attributesStr2attributesXhtml($attributes_str,$pattern='class',$with_content=false,$pattern_forbidden='') { if ($pattern != '') $attributes_keys = preg_split('#::#',$pattern); if ($pattern_forbidden != '') $pattern_forbidden = preg_split('#::#',$pattern_forbidden); //// attributesStr to attributesArray //// $attributes_content_array = preg_split('#::|;;#',$attributes_str,-1,PREG_SPLIT_OFFSET_CAPTURE); for ($i=0;$i<sizeof($attributes_content_array);$i++) { $type_position = $attributes_content_array[$i][1] - 1; // : or + if ($type_position < 0) $attributes_content_array[$i][1] = ':'; else $attributes_content_array[$i][1] = $attributes_str[$type_position]; } $first_is_not_an_attribute = trim($attributes_content_array[0][0]) == '' && $attributes_content_array[1][1] != ':'; $first_is_not_an_attribute = $first_is_not_an_attribute || $with_content; if ($first_is_not_an_attribute) { if ($with_content) $content = trim($attributes_content_array[0][0]); for ($i=1;$i<sizeof($attributes_content_array);$i++) { $attributes_array[$i-1] = $attributes_content_array[$i]; } } else $attributes_array = $attributes_content_array; unset($attributes_content_array); ////////////////////////////////////////// //// attributesArray to attributesXhtml //// $j=0; for ($i=0;$i<sizeof($attributes_array);$i++) { if ($attributes_array[$i][1] == ':') { if (isset($attributes_keys[$j])) { $attributes_final_array[$attributes_keys[$j]] = trim($attributes_array[$i][0]); $j++; } } elseif ($attributes_array[$i][1] == ';') { $regex = '#^\s*([a-zA-Z0-9]+)\s*[^a-zA-Z0-9](.*)$#'; if (preg_match($regex,$attributes_array[$i][0],$attribute)) { $attributes_final_array[trim($attribute[1])] = trim($attribute[2]); } } } $attributes_str = ''; if (is_array($attributes_final_array)) foreach ($attributes_final_array as $attribute_key => $attribute_value) { $forbidden = false; for ($i=0;$i<sizeof($pattern_forbidden);$i++) { if (preg_match('#'.preg_quote($attribute_key).'#i',$pattern_forbidden[$i])) { $forbidden = true; } } if (!$forbidden) { ## EMPTY VALUES PING if ($this->htmlentities($attribute_value) == '') { $attributes_str.= ' '.$this->htmlentities($attribute_key).'=two_double_quotation_marks'; } else { $attributes_str.= ' '.$this->htmlentities($attribute_key).'="'. $this->htmlentities($attribute_value).'"'; } } } //////////////////////////////////////////// if ($with_content) { $returned[0] = $content; $returned[1] = $attributes_str; } else $returned = $attributes_str; return $returned; } function liArray2liXhtml($li_array) { $li_xhtml = ''; for ($i=0;$i<sizeof($li_array);$i++) { // for each list (ul or ol) $tag = $li_array[$i][0]; $items_xhtml = ''; for ($j=0;$j<sizeof($li_array[$i][1]);$j++) { // for each item (li) if ($j>0) $items_xhtml.= "\n"; $content = $li_array[$i][1][$j][0]; $attributes = $li_array[$i][1][$j][2]; $items_xhtml.= '<li'.$attributes.'>'; $items_xhtml.= $content; if (is_array($li_array[$i][1][$j][1])) { // under-list $items_xhtml.= "\n"; $items_xhtml.= $this->tabulationner($this->liArray2liXhtml($li_array[$i][1][$j][1])); $items_xhtml.= "\n"; } $items_xhtml.=