A tumblelog CMS built on AJAX, PHP and MySQL.

textile.class.php 145KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082
  1. <?php
  2. // @(#) $Id: Textile.php,v 1.13 2005/03/21 15:26:55 jhriggs Exp $
  3. /* This program is free software; you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License as published by
  5. * the Free Software Foundation; either version 2 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with this program; if not, write to the Free Software
  15. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  16. */
  17. /**
  18. * The Textile class serves as a wrapper for all Textile
  19. * functionality. It is not inherently necessary that Textile be a
  20. * class; however, this is as close as one can get to a namespace in
  21. * PHP. Wrapping the functionality in a class prevents name
  22. * collisions and dirtying of the global namespace. The Textile class
  23. * uses no global variables and will not have any side-effects on
  24. * other code.
  25. *
  26. * @brief Class wrapper for the Textile functionality.
  27. */
  28. class Textile {
  29. /**
  30. * The @c array containing all of the Textile options for this
  31. * object.
  32. *
  33. * @private
  34. */
  35. var $options = array();
  36. /**
  37. * The @c string containing the regular expression pattern for a
  38. * URL. This variable is initialized by @c _create_re() which is
  39. * called in the contructor.
  40. *
  41. * @private
  42. */
  43. var $urlre;
  44. /**
  45. * The @c string containing the regular expression pattern for
  46. * punctuation characters. This variable is initialized by
  47. * @c _create_re() which is called in the contructor.
  48. *
  49. * @private
  50. */
  51. var $punct;
  52. /**
  53. * The @c string containing the regular expression pattern for the
  54. * valid vertical alignment codes. This variable is initialized by
  55. * @c _create_re() which is called in the contructor.
  56. *
  57. * @private
  58. */
  59. var $valignre;
  60. /**
  61. * The @c string containing the regular expression pattern for the
  62. * valid table alignment codes. This variable is initialized by
  63. * @c _create_re() which is called in the contructor.
  64. *
  65. * @private
  66. */
  67. var $tblalignre;
  68. /**
  69. * The @c string containing the regular expression pattern for the
  70. * valid horizontal alignment codes. This variable is initialized by
  71. * @c _create_re() which is called in the contructor.
  72. *
  73. * @private
  74. */
  75. var $halignre;
  76. /**
  77. * The @c string containing the regular expression pattern for the
  78. * valid alignment codes. This variable is initialized by
  79. * @c _create_re() which is called in the contructor.
  80. *
  81. * @private
  82. */
  83. var $alignre;
  84. /**
  85. * The @c string containing the regular expression pattern for the
  86. * valid image alignment codes. This variable is initialized by
  87. * @c _create_re() which is called in the contructor.
  88. *
  89. * @private
  90. */
  91. var $imgalignre;
  92. /**
  93. * The @c string containing the regular expression pattern for a
  94. * class, ID, and/or padding specification. This variable is
  95. * initialized by @c _create_re() which is called in the contructor.
  96. *
  97. * @private
  98. */
  99. var $clstypadre;
  100. /**
  101. * The @c string containing the regular expression pattern for a
  102. * class and/or ID specification. This variable is initialized by
  103. * @c _create_re() which is called in the contructor.
  104. *
  105. * @private
  106. */
  107. var $clstyre;
  108. /**
  109. * The @c string containing the regular expression pattern for a
  110. * class, ID, and/or filter specification. This variable is
  111. * initialized by @c _create_re() which is called in the contructor.
  112. *
  113. * @private
  114. */
  115. var $clstyfiltre;
  116. /**
  117. * The @c string containing the regular expression pattern for a
  118. * code block. This variable is initialized by @c _create_re() which
  119. * is called in the contructor.
  120. *
  121. * @private
  122. */
  123. var $codere;
  124. /**
  125. * The @c string containing the regular expression pattern for all
  126. * block tags. This variable is initialized by @c _create_re() which
  127. * is called in the contructor.
  128. *
  129. * @private
  130. */
  131. var $blocktags;
  132. /**
  133. * The @c array containing the list of lookup links.
  134. *
  135. * @private
  136. */
  137. var $links = array();
  138. /**
  139. * The @c array containing <code>array</code>s of replacement blocks
  140. * of text that are temporary removed from the input text to avoid
  141. * processing. Different functions use this replacement
  142. * functionality, and each shifts its own replacement array into
  143. * position 0 and removes it when finished. This avoids having
  144. * several replacement variables and/or functions clobbering
  145. * eachothers' replacement blocks.
  146. *
  147. * @private
  148. */
  149. var $repl = array();
  150. /**
  151. * The @c array containing temporary <code>string</code>s used in
  152. * replacement callbacks. *JHR*
  153. *
  154. * @private
  155. */
  156. var $tmp = array();
  157. /**
  158. * Instantiates a new Textile object. Optional options
  159. * can be passed to initialize the object. Attributes for the
  160. * options key are the same as the get/set method names
  161. * documented here.
  162. *
  163. * @param $options The @c array specifying the options to use for
  164. * this object.
  165. *
  166. * @public
  167. */
  168. function Textile($options = array()) {
  169. $this->options = $options;
  170. $this->options['filters'] = ($this->options['filters'] ? $this->options['filters'] : array());
  171. $this->options['charset'] = ($this->options['charset'] ? $this->options['charset'] : 'utf-8');
  172. $this->options['char_encoding'] = (isset($this->options['char_encoding']) ? $this->options['char_encoding'] : 1);
  173. $this->options['do_quotes'] = (isset($this->options['do_quotes']) ? $this->options['do_quotes'] : 1);
  174. $this->options['trim_spaces'] = (isset($this->options['trim_spaces']) ? $this->options['trim_spaces'] : 0);
  175. $this->options['smarty_mode'] = (isset($this->options['smarty_mode']) ? $this->options['smarty_mode'] : 1);
  176. $this->options['preserve_spaces'] = (isset($this->options['preserve_spaces']) ? $this->options['preserve_spaaces'] : 0);
  177. $this->options['head_offset'] = (isset($this->options['head_offset']) ? $this->options['head_offset'] : 0);
  178. if (is_array($this->options['css'])) {
  179. $this->css($this->options['css']);
  180. }
  181. $this->options['macros'] = ($this->options['macros'] ? $this->options['macros'] : $this->default_macros());
  182. if (isset($this->options['flavor'])) {
  183. $this->flavor($this->options['flavor']);
  184. } else {
  185. $this->flavor('xhtml1/css');
  186. }
  187. $this->_create_re();
  188. } // function Textile
  189. // getter/setter methods...
  190. /**
  191. * Used to set Textile attributes. Attribute names are the same
  192. * as the get/set method names documented here.
  193. *
  194. * @param $opt A @c string specifying the name of the option to
  195. * change or an @c array specifying options and values.
  196. * @param $value The value for the provided option name.
  197. *
  198. * @public
  199. */
  200. function set($opt, $value = NULL) {
  201. if (is_array($opt)) {
  202. foreach ($opt as $opt => $value) {
  203. $this->set($opt, $value);
  204. }
  205. } else {
  206. // the following options have special set methods
  207. // that activate upon setting:
  208. if ($opt == 'charset') {
  209. $this->charset($value);
  210. } elseif ($opt == 'css') {
  211. $this->css($value);
  212. } elseif ($opt == 'flavor') {
  213. $this->flavor($value);
  214. } else {
  215. $this->options[$opt] = $value;
  216. }
  217. }
  218. } // function set
  219. /**
  220. * Used to get Textile attributes. Attribute names are the same
  221. * as the get/set method names documented here.
  222. *
  223. * @param $opt A @c string specifying the name of the option to get.
  224. *
  225. * @return The value for the provided option.
  226. *
  227. * @public
  228. */
  229. function get($opt) {
  230. return $this->options[$opt];
  231. } // function get
  232. /**
  233. * Gets or sets the "disable html" control, which allows you to
  234. * prevent HTML tags from being used within the text processed.
  235. * Any HTML tags encountered will be removed if disable html is
  236. * enabled. Default behavior is to allow HTML.
  237. *
  238. * @param $disable_html If provided, a @c bool indicating whether or
  239. * not this object should disable HTML.
  240. *
  241. * @return A true value if this object disables HTML; a false value
  242. * otherwise.
  243. *
  244. * @public
  245. */
  246. function disable_html($disable_html = NULL) {
  247. if ($disable_html != NULL) {
  248. $this->options['disable_html'] = $disable_html;
  249. }
  250. return ($this->options['disable_html'] ? $this->options['disable_html'] : 0);
  251. } // function disable_html
  252. /**
  253. * Gets or sets the relative heading offset, which allows you to
  254. * change the heading level used within the text processed. For
  255. * example, if the heading offset is '2' and the text contains an
  256. * 'h1' block, an \<h3\> block will be output.
  257. *
  258. * @param $head_offset If provided, an @c integer specifying the
  259. * heading offset for this object.
  260. *
  261. * @return An @c integer containing the heading offset for this
  262. * object.
  263. *
  264. * @public
  265. */
  266. function head_offset($head_offset = NULL) {
  267. if ($head_offset != NULL) {
  268. $this->options['head_offset'] = $head_offset;
  269. }
  270. return ($this->options['head_offset'] ? $this->options['head_offset'] : 0);
  271. } // function head_offset
  272. /**
  273. * Assigns the HTML flavor of output from Textile. Currently
  274. * these are the valid choices: html, xhtml (behaves like "xhtml1"),
  275. * xhtml1, xhtml2. Default flavor is "xhtml1".
  276. *
  277. * Note that the xhtml2 flavor support is experimental and incomplete
  278. * (and will remain that way until the XHTML 2.0 draft becomes a
  279. * proper recommendation).
  280. *
  281. * @param $flavor If provided, a @c string specifying the flavor to
  282. * be used for this object.
  283. *
  284. * @return A @c string containing the flavor for this object.
  285. *
  286. * @public
  287. */
  288. function flavor($flavor = NULL) {
  289. if ($flavor != NULL) {
  290. $this->options['flavor'] = $flavor;
  291. if (preg_match('/^xhtml(\d)?(\D|$)/', $flavor, $matches)) {
  292. if ($matches[1] == '2') {
  293. $this->options['_line_open'] = '<l>';
  294. $this->options['_line_close'] = '</l>';
  295. $this->options['_blockcode_open'] = '<blockcode>';
  296. $this->options['_blockcode_close'] = '</blockcode>';
  297. $this->options['css_mode'] = 1;
  298. } else {
  299. // xhtml 1.x
  300. $this->options['_line_open'] = '';
  301. $this->options['_line_close'] = '<br />';
  302. $this->options['_blockcode_open'] = '<pre><code>';
  303. $this->options['_blockcode_close'] = '</code></pre>';
  304. $this->options['css_mode'] = 1;
  305. }
  306. } elseif (preg_match('/^html/', $flavor)) {
  307. $this->options['_line_open'] = '';
  308. $this->options['_line_close'] = '<br>';
  309. $this->options['_blockcode_open'] = '<pre><code>';
  310. $this->options['_blockcode_close'] = '</code></pre>';
  311. $this->options['css_mode'] = preg_match('/\/css/', $flavor);
  312. }
  313. if ($this->options['css_mode'] && !isset($this->options['css'])) { $this->_css_defaults(); }
  314. }
  315. return $this->options['flavor'];
  316. } // function flavor
  317. /**
  318. * Gets or sets the css support for Textile. If css is enabled,
  319. * Textile will emit CSS rules. You may pass a 1 or 0 to enable
  320. * or disable CSS behavior altogether. If you pass an associative array,
  321. * you may assign the CSS class names that are used by
  322. * Textile. The following key names for such an array are
  323. * recognized:
  324. *
  325. * <ul>
  326. * <li><b>class_align_right</b>
  327. *
  328. * defaults to 'right'</li>
  329. *
  330. * <li><b>class_align_left</b>
  331. *
  332. * defaults to 'left'</li>
  333. *
  334. * <li><b>class_align_center</b>
  335. *
  336. * defaults to 'center'</li>
  337. *
  338. * <li><b>class_align_top</b>
  339. *
  340. * defaults to 'top'</li>
  341. *
  342. * <li><b>class_align_bottom</b>
  343. *
  344. * defaults to 'bottom'</li>
  345. *
  346. * <li><b>class_align_middle</b>
  347. *
  348. * defaults to 'middle'</li>
  349. *
  350. * <li><b>class_align_justify</b>
  351. *
  352. * defaults to 'justify'</li>
  353. *
  354. * <li><b>class_caps</b>
  355. *
  356. * defaults to 'caps'</li>
  357. *
  358. * <li><b>class_footnote</b>
  359. *
  360. * defaults to 'footnote'</li>
  361. *
  362. * <li><b>id_footnote_prefix</b>
  363. *
  364. * defaults to 'fn'</li>
  365. *
  366. * </ul>
  367. *
  368. * @param $css If provided, either a @c bool indicating whether or
  369. * not this object should use css or an associative @c array
  370. * specifying class names to use.
  371. *
  372. * @return Either an associative @c array containing class names
  373. * used by this object, or a true or false value indicating
  374. * whether or not this object uses css.
  375. *
  376. * @public
  377. */
  378. function css($css = NULL) {
  379. if ($css != NULL) {
  380. if (is_array($css)) {
  381. $this->options['css'] = $css;
  382. $this->options['css_mode'] = 1;
  383. } else {
  384. $this->options['css_mode'] = $css;
  385. if ($this->options['css_mode'] && !isset($this->options['css'])) { $this->_css_defaults(); }
  386. }
  387. }
  388. return ($this->options['css_mode'] ? $this->options['css'] : 0);
  389. } // function css
  390. /**
  391. * Gets or sets the character set targetted for publication.
  392. * At this time, Textile only changes its behavior
  393. * if the 'utf-8' character set is assigned.
  394. *
  395. * Specifically, if utf-8 is requested, any special characters
  396. * created by Textile will be output as native utf-8 characters
  397. * rather than HTML entities.
  398. *
  399. * @param $charset If provided, a @c string specifying the
  400. * characater set to be used for this object.
  401. *
  402. * @return A @c string containing the character set for this object.
  403. *
  404. * @public
  405. */
  406. function charset($charset = NULL) {
  407. if ($charset != NULL) {
  408. $this->options['charset'] = $charset;
  409. if (preg_match('/^utf-?8$/i', $this->options['charset'])) {
  410. $this->char_encoding(0);
  411. } else {
  412. $this->char_encoding(1);
  413. }
  414. }
  415. return $this->options['charset'];
  416. } // function charset
  417. /**
  418. * Gets or sets the physical file path to root of document files.
  419. * This path is utilized when images are referenced and size
  420. * calculations are needed (the getimagesize() function is used to read
  421. * the image dimensions).
  422. *
  423. * @param $docroot If provided, a @c string specifying the document
  424. * root to use for this object.
  425. *
  426. * @return A @c string containing the docroot for this object.
  427. *
  428. * @public
  429. */
  430. function docroot($docroot = NULL) {
  431. if ($docroot != NULL) {
  432. $this->options['docroot'] = $docroot;
  433. }
  434. return $this->options['docroot'];
  435. } // function docroot
  436. /**
  437. * Gets or sets the 'trim spaces' control flag. If enabled, this
  438. * will clear any lines that have only spaces on them (the newline
  439. * itself will remain).
  440. *
  441. * @param $trim_spaces If provided, a @c bool indicating whether or
  442. * not this object should trim spaces.
  443. *
  444. * @return A true value if this object trims spaces; a false value
  445. * otherwise.
  446. *
  447. * @public
  448. */
  449. function trim_spaces($trim_spaces = NULL) {
  450. if ($trim_spaces != NULL) {
  451. $this->options['trim_spaces'] = $trim_spaces;
  452. }
  453. return $this->options['trim_spaces'];
  454. } // function trim_spaces
  455. /**
  456. * Gets or sets a parameter that is passed to filters.
  457. *
  458. * @param $filter_param If provided, a parameter that this object
  459. * should pass to filters.
  460. *
  461. * @return The parameter this object passes to filters.
  462. *
  463. * @public
  464. */
  465. function filter_param($filter_param = NULL) {
  466. if ($filter_param != NULL) {
  467. $this->options['filter_param'] = $filter_param;
  468. }
  469. return $this->options['filter_param'];
  470. } // function filter_param
  471. /**
  472. * Gets or sets the 'preserve spaces' control flag. If enabled, this
  473. * will replace any double spaces within the paragraph data with the
  474. * \&amp;#8195; HTML entity (wide space). The default is 0. Spaces will
  475. * pass through to the browser unchanged and render as a single space.
  476. * Note that this setting has no effect on spaces within \<pre\>,
  477. * \<code\> blocks or \<script\> sections.
  478. *
  479. * @param $preserve_spaces If provided, a @c bool indicating whether
  480. * or not this object should preserve spaces.
  481. *
  482. * @return A true value if this object preserves spaces; a false
  483. * value otherwise.
  484. *
  485. * @public
  486. */
  487. function preserve_spaces($preserve_spaces = NULL) {
  488. if ($preserve_spaces != NULL) {
  489. $this->options['preserve_spaces'] = $preserve_spaces;
  490. }
  491. return $this->options['preserve_spaces'];
  492. } // function preserve_spaces
  493. /**
  494. * Gets or sets a list of filters to make available for
  495. * Textile to use. Returns a hash reference of the currently
  496. * assigned filters.
  497. *
  498. * @param $filters If provided, an @c array of filters to be used
  499. * for this object.
  500. *
  501. * @return An @c array containing the filters for this object.
  502. *
  503. * @public
  504. */
  505. function filters($filters = NULL) {
  506. if ($filters != NULL) {
  507. $this->options['filters'] = $filters;
  508. }
  509. return $this->options['filters'];
  510. } // function filters
  511. /**
  512. * Gets or sets the character encoding logical flag. If character
  513. * encoding is enabled, the htmlentities function is used to
  514. * encode special characters. If character encoding is disabled,
  515. * only \<, \>, " and & are encoded to HTML entities.
  516. *
  517. * @param $char_encoding If provided, a @c bool indicating whether
  518. * or not this object should encode special characters.
  519. *
  520. * @return A true value if this object encodes special characters; a
  521. * false value otherwise.
  522. *
  523. * @public
  524. */
  525. function char_encoding($char_encoding = NULL) {
  526. if ($char_encoding !== NULL) {
  527. $this->options['char_encoding'] = $char_encoding;
  528. }
  529. return $this->options['char_encoding'];
  530. } // function char_encoding
  531. /**
  532. * Gets or sets the "smart quoting" control flag. Returns the
  533. * current setting.
  534. *
  535. * @param $do_quotes If provided, a @c bool indicating whether or
  536. * not this object should use smart quoting.
  537. *
  538. * @return A true value if this object uses smart quoting; a false
  539. * value otherwise.
  540. *
  541. * @public
  542. */
  543. function handle_quotes($do_quotes = NULL) {
  544. if ($do_quotes != NULL) {
  545. $this->options['do_quotes'] = $do_quotes;
  546. }
  547. return $this->options['do_quotes'];
  548. } // function handle_quotes
  549. // end of getter/setter methods
  550. /**
  551. * Creates the class variable regular expression patterns used by
  552. * Textile. They are not initialized in the declaration, because
  553. * some rely on the others, requiring a @c $this reference.
  554. *
  555. * PHP does not have the Perl qr operator to quote or precompile
  556. * patterns, so to avoid escaping and matching problems, all
  557. * patterns must use the same delimiter; this implementation uses
  558. * {}. Every use of these patterns within this class has been
  559. * changed to use these delimiters. *JHR*
  560. *
  561. * @private
  562. */
  563. function _create_re() {
  564. // a URL discovery regex. This is from Mastering Regex from O'Reilly.
  565. // Some modifications by Brad Choate <brad at bradchoate dot com>
  566. $this->urlre = '(?:
  567. # Must start out right...
  568. (?=[a-zA-Z0-9./#])
  569. # Match the leading part (proto://hostname, or just hostname)
  570. (?:
  571. # ftp://, http://, or https:// leading part
  572. (?:ftp|https?|telnet|nntp)://(?:\w+(?::\w+)?@)?[-\w]+(?:\.\w[-\w]*)+
  573. |
  574. (?:mailto:)?[-\+\w]+@[-\w]+(?:\.\w[-\w]*)+
  575. |
  576. # or, try to find a hostname with our more specific sub-expression
  577. (?i: [a-z0-9] (?:[-a-z0-9]*[a-z0-9])? \. )+ # sub domains
  578. # Now ending .com, etc. For these, require lowercase
  579. (?-i: com\b
  580. | edu\b
  581. | biz\b
  582. | gov\b
  583. | in(?:t|fo)\b # .int or .info
  584. | mil\b
  585. | net\b
  586. | org\b
  587. | museum\b
  588. | aero\b
  589. | coop\b
  590. | name\b
  591. | pro\b
  592. | [a-z][a-z]\b # two-letter country codes
  593. )
  594. )?
  595. # Allow an optional port number
  596. (?: : \d+ )?
  597. # The rest of the URL is optional, and begins with / . . .
  598. (?:
  599. /?
  600. # The rest are heuristics for what seems to work well
  601. [^.!,?;:"\'<>()\[\]{}\s\x7F-\xFF]*
  602. (?:
  603. [.!,?;:]+ [^.!,?;:"\'<>()\[\]{}\s\x7F-\xFF]+ #\'"
  604. )*
  605. )?
  606. )';
  607. $this->punct = '[\!"\#\$%&\'()\*\+,\-\./:;<=>\?@\[\\\\\]\^_`{\|}\~]';
  608. $this->valignre = '[\-^~]';
  609. $this->tblalignre = '[<>=]';
  610. $this->halignre = '(?:<>|[<>=])';
  611. $this->alignre = '(?:(?:' . $this->valignre . '|<>' . $this->valignre . '?|' . $this->valignre . '?<>|' . $this->valignre . '?' . $this->halignre . '?|' . $this->halignre . '?' . $this->valignre . '?)(?!\w))';
  612. $this->imgalignre = '(?:(?:[<>]|' . $this->valignre . '){1,2})';
  613. $this->clstypadre = '(?:
  614. (?:\([A-Za-z0-9_\- \#]+\))
  615. |
  616. (?:{
  617. (?: \( [^)]+ \) | [^\}] )+
  618. })
  619. |
  620. (?:\(+? (?![A-Za-z0-9_\-\#]) )
  621. |
  622. (?:\)+?)
  623. |
  624. (?: \[ [a-zA-Z\-]+? \] )
  625. )';
  626. $this->clstyre = '(?:
  627. (?:\([A-Za-z0-9_\- \#]+\))
  628. |
  629. (?:{
  630. [A-Za-z0-9_\-](?: \( [^)]+ \) | [^\}] )+
  631. })
  632. |
  633. (?: \[ [a-zA-Z\-]+? \] )
  634. )';
  635. $this->clstyfiltre = '(?:
  636. (?:\([A-Za-z0-9_\- \#]+\))
  637. |
  638. (?:{
  639. [A-Za-z0-9_\-](?: \( [^)]+ \) | [^\}] )+
  640. })
  641. |
  642. (?:\|[^\|]+\|)
  643. |
  644. (?:\(+?(?![A-Za-z0-9_\-\#]))
  645. |
  646. (?:\)+)
  647. |
  648. (?: \[ [a-zA-Z]+? \] )
  649. )';
  650. $this->codere = '(?:
  651. (?:
  652. [\[{]
  653. @ # opening
  654. (?:\[([A-Za-z0-9]+)\])? # $1: language id
  655. (.+?) # $2: code
  656. @ # closing
  657. [\]}]
  658. )
  659. |
  660. (?:
  661. (?:^|(?<=[\s\(]))
  662. @ # opening
  663. (?:\[([A-Za-z0-9]+)\])? # $3: language id
  664. ([^\s].+?[^\s]) # $4: code itself
  665. @ # closing
  666. (?:$|(?=' . $this->punct . '{1,2}|\s))
  667. )
  668. )';
  669. $this->blocktags = '
  670. <
  671. (( /? ( h[1-6]
  672. | p
  673. | pre
  674. | div
  675. | table
  676. | t[rdh]
  677. | [ou]l
  678. | li
  679. | block(?:quote|code)
  680. | form
  681. | input
  682. | select
  683. | option
  684. | textarea
  685. )
  686. [ >]
  687. )
  688. | !--
  689. )
  690. ';
  691. } // function _create_re
  692. /**
  693. * Transforms the provided text using Textile markup rules.
  694. *
  695. * @param $str The @c string specifying the text to process.
  696. *
  697. * @return A @c string containing the processed (X)HTML.
  698. *
  699. * @public
  700. */
  701. function process($str) {
  702. /*
  703. * Function names in PHP are case insensitive, so function
  704. * textile() cannot be redefined. Thus, this PHP implementation
  705. * will only use process().
  706. *
  707. * return $this->textile($str);
  708. * } // function process
  709. *
  710. * function textile($str) {
  711. */
  712. $this->charset('utf8');
  713. // quick translator for abbreviated block names
  714. // to their tag
  715. $macros = array('bq' => 'blockquote');
  716. // an array to hold any portions of the text to be preserved
  717. // without further processing by Textile
  718. array_unshift($this->repl, array());
  719. // strip out extra newline characters. we're only matching for \n herein
  720. //$str = preg_replace('!(?:\r?\n|\r)!', "\n", $str);
  721. $str = preg_replace('!(?:\015?\012|\015)!', "\n", $str);
  722. // optionally remove trailing spaces
  723. if ($this->options['trim_spaces']) { $str = preg_replace('/ +$/m', '', $str); }
  724. // preserve contents of the '==', 'pre', 'blockcode' sections
  725. $str = preg_replace_callback('{(^|\n\n)==(.+?)==($|\n\n)}s',
  726. $this->_cb('"$m[1]\n\n" . $me->_repl($me->repl[0], $me->format_block(array("text" => $m[2]))) . "\n\n$m[3]"'), $str);
  727. if (!$this->disable_html()) {
  728. // preserve style, script tag contents
  729. $str = preg_replace_callback('!(<(style|script)(?:>| .+?>).*?</\2>)!s', $this->_cb('$me->_repl($me->repl[0], $m[1])'), $str);
  730. // preserve HTML comments
  731. $str = preg_replace_callback('|(<!--.+?-->)|s', $this->_cb('$me->_repl($me->repl[0], $m[1])'), $str);
  732. // preserve pre block contents, encode contents by default
  733. $pre_start = count($this->repl[0]);
  734. $str = preg_replace_callback('{(<pre(?: [^>]*)?>)(.+?)(</pre>)}s',
  735. $this->_cb('"\n\n" . $me->_repl($me->repl[0], $m[1] . $me->encode_html($m[2], 1) . $m[3]) . "\n\n"'), $str);
  736. // fix code tags within pre blocks we just saved.
  737. for ($i = $pre_start; $i < count($this->repl[0]); $i++) {
  738. $this->repl[0][$i] = preg_replace('|&lt;(/?)code(.*?)&gt;|s', '<$1code$2>', $this->repl[0][$i]);
  739. }
  740. // preserve code blocks by default, encode contents
  741. $str = preg_replace_callback('{(<code(?: [^>]+)?>)(.+?)(</code>)}s',
  742. $this->_cb('$me->_repl($me->repl[0], $m[1] . $me->encode_html($m[2], 1) . $m[3])'), $str);
  743. // encode blockcode tag (an XHTML 2 tag) and encode it's
  744. // content by default
  745. $str = preg_replace_callback('{(<blockcode(?: [^>]+)?>)(.+?)(</blockcode>)}s',
  746. $this->_cb('"\n\n" . $me->_repl($me->repl[0], $m[1] . $me->encode_html($m[2], 1) . $m[3]) . "\n\n"'), $str);
  747. // preserve PHPish, ASPish code
  748. $str = preg_replace_callback('!(<([\?%]).*?(\2)>)!s', $this->_cb('$me->_repl($me->repl[0], $m[1])'), $str);
  749. }
  750. // pass through and remove links that follow this format
  751. // [id_without_spaces (optional title text)]url
  752. // lines like this are stripped from the content, and can be
  753. // referred to using the "link text":id_without_spaces syntax
  754. //$links = array();
  755. $str = preg_replace_callback('{(?:\n|^) [ ]* \[ ([^ ]+?) [ ]*? (?:\( (.+?) \) )? \] ((?:(?:ftp|https?|telnet|nntp)://|/)[^ ]+?) [ ]* (\n|$)}mx',
  756. $this->_cb('($me->links[$m[1]] = array("url" => $m[3], "title" => $m[2])) ? $m[4] : $m[4]'), $str);
  757. //$this->links = $links;
  758. // eliminate starting/ending blank lines
  759. $str = preg_replace('/^\n+/s', '', $str, 1);
  760. $str = preg_replace('/\n+$/s', '', $str, 1);
  761. // split up text into paragraph blocks, capturing newlines too
  762. $para = preg_split('/(\n{2,})/', $str, -1, PREG_SPLIT_DELIM_CAPTURE);
  763. unset($block, $bqlang, $filter, $class, $sticky, $lines,
  764. $style, $stickybuff, $lang, $clear);
  765. $out = '';
  766. foreach ($para as $para) {
  767. if (preg_match('/^\n+$/s', $para)) {
  768. if ($sticky && $stickybuff) {
  769. $stickybuff .= $para;
  770. } else {
  771. $out .= $para;
  772. }
  773. continue;
  774. }
  775. if ($sticky) {
  776. $sticky++;
  777. } else {
  778. unset($block);
  779. unset($class);
  780. $style = '';
  781. unset($lang);
  782. }
  783. unset($id, $cite, $align, $padleft, $padright, $lines, $buffer);
  784. if (preg_match('{^(h[1-6]|p|bq|bc|fn\d+)
  785. ((?:' . $this->clstyfiltre . '*|' . $this->halignre . ')*)
  786. (\.\.?)
  787. (?::(\d+|' . $this->urlre . '))?\ (.*)$}sx', $para, $matches)) {
  788. if ($sticky) {
  789. if ($block == 'bc') {
  790. // close our blockcode section
  791. $out = preg_replace('/\n\n$/', '', $out, 1);
  792. $out .= $this->options['_blockcode_close'] . "\n\n";
  793. } elseif ($block == 'bq') {
  794. $out = preg_replace('/\n\n$/', '', $out, 1);
  795. $out .= '</blockquote>' . "\n\n";
  796. } elseif ($block == 'table') {
  797. $table_out = $this->format_table(array('text' => $stickybuff));
  798. if (!$table_out) { $table_out = ''; }
  799. $out .= $table_out;
  800. unset($stickybuff);
  801. } elseif ($block == 'dl') {
  802. $dl_out = $this->format_deflist(array('text' => $stickybuff));
  803. if (!$dl_out) { $dl_out = ''; }
  804. $out .= $dl_out;
  805. unset($stickybuff);
  806. }
  807. $sticky = 0;
  808. }
  809. // block macros: h[1-6](class)., bq(class)., bc(class)., p(class).
  810. //warn "paragraph: [[$para]]\n\tblock: $1\n\tparams: $2\n\tcite: $4";
  811. $block = $matches[1];
  812. $params = $matches[2];
  813. $cite = $matches[4];
  814. if ($matches[3] == '..') {
  815. $sticky = 1;
  816. } else {
  817. $sticky = 0;
  818. unset($class);
  819. unset($bqlang);
  820. unset($lang);
  821. $style = '';
  822. unset($filter);
  823. }
  824. if (preg_match('/^h([1-6])$/', $block, $matches2)) {
  825. if ($this->options['head_offset']) {
  826. $block = 'h' . ($matches2[1] + $this->options['head_offset']);
  827. }
  828. }
  829. if (preg_match('{(' . $this->halignre . '+)}', $params, $matches2)) {
  830. $align = $matches2[1];
  831. $params = preg_replace('{' . $this->halignre . '+}', '', $params, 1);
  832. }
  833. if ($params) {
  834. if (preg_match('/\|(.+)\|/', $params, $matches2)) {
  835. $filter = $matches2[1];
  836. $params = preg_replace('/\|.+?\|/', '', $params, 1);
  837. }
  838. if (preg_match('/{([^}]+)}/', $params, $matches2)) {
  839. $style = $matches2[1];
  840. $style = preg_replace('/\n/', ' ', $style);
  841. $params = preg_replace('/{[^}]+}/', '', $params);
  842. }
  843. if (preg_match('/\(([A-Za-z0-9_\-\ ]+?)(?:\#(.+?))?\)/', $params, $matches2) ||
  844. preg_match('/\(([A-Za-z0-9_\-\ ]+?)?(?:\#(.+?))\)/', $params, $matches2)) {
  845. if ($matches2[1] || $matches2[2]) {
  846. $class = $matches2[1];
  847. $id = $matches2[2];
  848. if ($class) {
  849. $params = preg_replace('/\([A-Za-z0-9_\-\ ]+?(#.*?)?\)/', '', $params);
  850. } elseif ($id) {
  851. $params = preg_replace('/\(#.+?\)/', '', $params);
  852. }
  853. }
  854. }
  855. if (preg_match('/(\(+)/', $params, $matches2)) {
  856. $padleft = strlen($matches2[1]);
  857. $params = preg_replace('/\(+/', '', $params, 1);
  858. }
  859. if (preg_match('/(\)+)/', $params, $matches2)) {
  860. $padright = strlen($matches2[1]);
  861. $params = preg_replace('/\)+/', '', $params, 1);
  862. }
  863. if (preg_match('/\[(.+?)\]/', $params, $matches2)) {
  864. $lang = $matches2[1];
  865. if ($block == 'bc') {
  866. $bqlang = $lang;
  867. unset($lang);
  868. }
  869. $params = preg_replace('/\[.+?\]/', '', $params, 1);
  870. }
  871. }
  872. // warn "settings:\n\tblock: $block\n\tpadleft: $padleft\n\tpadright: $padright\n\tclass: $class\n\tstyle: $style\n\tid: $id\n\tfilter: $filter\n\talign: $align\n\tlang: $lang\n\tsticky: $sticky";
  873. $para = $matches[5];
  874. } elseif (preg_match('|^<textile#(\d+)>$|', $para, $matches)) {
  875. $buffer = $this->repl[0][$matches[1] - 1];
  876. } elseif (preg_match('/^clear([<>]+)?\.$/', $para, $matches)) {
  877. if ($matches[1] == '<') {
  878. $clear = 'left';
  879. } elseif ($matches[1] == '>') {
  880. $clear = 'right';
  881. } else {
  882. $clear = 'both';
  883. }
  884. continue;
  885. } elseif ($sticky && $stickybuff &&
  886. ($block == 'table' || $block == 'dl')) {
  887. $stickybuff .= $para;
  888. continue;
  889. } elseif (preg_match('{^(?:' . $this->halignre . '|' . $this->clstypadre . '*)*
  890. [\*\#]
  891. (?:' . $this->halignre . '|' . $this->clstypadre . '*)*
  892. \ }x', $para)) {
  893. // '*', '#' prefix means a list
  894. $buffer = $this->format_list(array('text' => $para));
  895. } elseif (preg_match('{^(?:table(?:' . $this->tblalignre . '|' . $this->clstypadre . '*)*
  896. (\.\.?)\s+)?
  897. (?:_|' . $this->alignre . '|' . $this->clstypadre . '*)*\|}x', $para, $matches)) {
  898. // handle wiki-style tables
  899. if ($matches[1] && ($matches[1] == '..')) {
  900. $block = 'table';
  901. $stickybuff = $para;
  902. $sticky = 1;
  903. continue;
  904. } else {
  905. $buffer = $this->format_table(array('text' => $para));
  906. }
  907. } elseif (preg_match('{^(?:dl(?:' . $this->clstyre . ')*(\.\.?)\s+)}x', $para, $matches)) {
  908. // handle definition lists
  909. if ($matches[1] && ($matches[1] == '..')) {
  910. $block = 'dl';
  911. $stickybuff = $para;
  912. $sticky = 1;
  913. continue;
  914. } else {
  915. $buffer = $this->format_deflist(array('text' => $para));
  916. }
  917. }
  918. if ($buffer) {
  919. $out .= $buffer;
  920. continue;
  921. }
  922. $lines = preg_split('/\n/', $para);
  923. if ((count($lines) == 1) && ($lines[0] == '')) {
  924. continue;
  925. }
  926. $block = ($block ? $block : 'p');
  927. $buffer = '';
  928. $pre = '';
  929. $post = '';
  930. if ($block == 'bc') {
  931. if ($sticky <= 1) {
  932. $pre .= $this->options['_blockcode_open'];
  933. $pre = preg_replace('/>$/s', '', $pre, 1);
  934. if ($bqlang) { $pre .= " language=\"$bqlang\""; }
  935. if ($align) {
  936. $alignment = $this->_halign($align);
  937. if ($this->options['css_mode']) {
  938. if (($padleft || $padright) &&
  939. (($alignment == 'left') || ($alignment == 'right'))) {
  940. $style .= ';float:' . $alignment;
  941. } else {
  942. $style .= ';text-align:' . $alignment;
  943. }
  944. $class .= ' ' . ($this->options['css']["class_align_$alignment"] ? $this->options['css']["class_align_$alignment"] : $alignment);
  945. } else {
  946. if ($alignment) { $pre .= " align=\"$alignment\""; }
  947. }
  948. }
  949. if ($padleft) { $style .= ";padding-left:${padleft}em"; }
  950. if ($padright) { $style .= ";padding-right:${padright}em"; }
  951. if ($clear) { $style .= ";clear:${clear}"; }
  952. if ($class) { $class = preg_replace('/^ /', '', $class, 1); }
  953. if ($class) { $pre .= " class=\"$class\""; }
  954. if ($id) { $pre .= " id=\"$id\""; }
  955. if ($style) { $style = preg_replace('/^;/', '', $style, 1); }
  956. if ($style) { $pre .= " style=\"$style\""; }
  957. if ($lang) { $pre .= " lang=\"$lang\""; }
  958. $pre .= '>';
  959. unset($lang);
  960. unset($bqlang);
  961. unset($clear);
  962. }
  963. $para = preg_replace_callback('{(?:^|(?<=[\s>])|([{[]))
  964. ==(.+?)==
  965. (?:$|([\]}])|(?=' . $this->punct . '{1,2}|\s))}sx',
  966. $this->_cb('$me->_repl($me->repl[0], $me->format_block(array("text" => $m[2], "inline" => 1, "pre" => $m[1], "post" => $m[3])))'), $para);
  967. $buffer .= $this->encode_html_basic($para, 1);
  968. $buffer = preg_replace('/&lt;textile#(\d+)&gt;/', '<textile#$1>', $buffer);
  969. if ($sticky == 0) {
  970. $post .= $this->options['_blockcode_close'];
  971. }
  972. $out .= $pre . $buffer . $post;
  973. continue;
  974. } elseif ($block == 'bq') {
  975. if ($sticky <= 1) {
  976. $pre .= '<blockquote';
  977. if ($align) {
  978. $alignment = $this->_halign($align);
  979. if ($this->options['css_mode']) {
  980. if (($padleft || $padright) &&
  981. (($alignment == 'left') || ($alignment == 'right'))) {
  982. $style .= ';float:' . $alignment;
  983. } else {
  984. $style .= ';text-align:' . $alignment;
  985. }
  986. $class .= ' ' . ($this->options['css']["class_align_$alignment"] ? $this->options['css']["class_align_$alignment"] : $alignment);
  987. } else {
  988. if ($alignment) { $pre .= " align=\"$alignment\""; }
  989. }
  990. }
  991. if ($padleft) { $style .= ";padding-left:${padleft}em"; }
  992. if ($padright) { $style .= ";padding-right:${padright}em"; }
  993. if ($clear) { $style .= ";clear:${clear}"; }
  994. if ($class) { $class = preg_replace('/^ /', '', $class, 1); }
  995. if ($class) { $pre .= " class=\"$class\""; }
  996. if ($id) { $pre .= " id=\"$id\""; }
  997. if ($style) { $style = preg_replace('/^;/', '', $style, 1); }
  998. if ($style) { $pre .= " style=\"$style\""; }
  999. if ($lang) { $pre .= " lang=\"$lang\""; }
  1000. if ($cite) { $pre .= ' cite="' . $this->format_url(array('url' => $cite)) . '"'; }
  1001. $pre .= '>';
  1002. unset($clear);
  1003. }
  1004. $pre .= '<p>';
  1005. } elseif (preg_match('/fn(\d+)/', $block, $matches)) {
  1006. $fnum = $matches[1];
  1007. $pre .= '<p';
  1008. if ($this->options['css']['class_footnote']) { $class .= ' ' . $this->options['css']['class_footnote']; }
  1009. if ($align) {
  1010. $alignment = $this->_halign($align);
  1011. if ($this->options['css_mode']) {
  1012. if (($padleft || $padright) &&
  1013. (($alignment == 'left') || ($alignment == 'right'))) {
  1014. $style .= ';float:' . $alignment;
  1015. } else {
  1016. $style .= ';text-align:' . $alignment;
  1017. }
  1018. $class .= ($this->options['css']["class_align_$alignment"] ? $this->options['css']["class_align_$alignment"] : $alignment);
  1019. } else {
  1020. $pre .= " align=\"$alignment\"";
  1021. }
  1022. }
  1023. if ($padleft) { $style .= ";padding-left:${padleft}em"; }
  1024. if ($padright) { $style .= ";padding-right:${padright}em"; }
  1025. if ($clear) { $style .= ";clear:${clear}"; }
  1026. if ($class) { $class = preg_replace('/^ /', '', $class, 1); }
  1027. if ($class) { $pre .= " class=\"$class\""; }
  1028. $pre .= ' id="' . ($this->options['css']['id_footnote_prefix'] ? $this->options['css']['id_footnote_prefix'] : 'fn') . $fnum . '"';
  1029. if ($style) { $style = preg_replace('/^;/', '', $style, 1); }
  1030. if ($style) { $pre .= " style=\"$style\""; }
  1031. if ($lang) { $pre .= " lang=\"$lang\""; }
  1032. $pre .= '>';
  1033. $pre .= '<sup>' . $fnum . '</sup> ';
  1034. // we can close like a regular paragraph tag now
  1035. $block = 'p';
  1036. unset($clear);
  1037. } else {
  1038. $pre .= '<' . ($macros[$block] ? $macros[$block] : $block);
  1039. if ($align) {
  1040. $alignment = $this->_halign($align);
  1041. if ($this->options['css_mode']) {
  1042. if (($padleft || $padright) &&
  1043. (($alignment == 'left') || ($alignment == 'right'))) {
  1044. $style .= ';float:' . $alignment;
  1045. } else {
  1046. $style .= ';text-align:' . $alignment;
  1047. }
  1048. $class .= ' ' . ($this->options['css']["class_align_$alignment"] ? $this->options['css']["class_align_$alignment"] : $alignment);
  1049. } else {
  1050. $pre .= " align=\"$alignment\"";
  1051. }
  1052. }
  1053. if ($padleft) { $style .= ";padding-left:${padleft}em"; }
  1054. if ($padright) { $style .= ";padding-right:${padright}em"; }
  1055. if ($clear) { $style .= ";clear:${clear}"; }
  1056. if ($class) { $class = preg_replace('/^ /', '', $class, 1); }
  1057. if ($class) { $pre .= " class=\"$class\""; }
  1058. if ($id) { $pre .= " id=\"$id\""; }
  1059. if ($style) { $style = preg_replace('/^;/', '', $style, 1); }
  1060. if ($style) { $pre .= " style=\"$style\""; }
  1061. if ($lang) { $pre .= " lang=\"$lang\""; }
  1062. if ($cite && ($block == 'bq')) { $pre .= ' cite="' . $this->format_url(array('url' => $cite)) . '"'; }
  1063. $pre .= '>';
  1064. unset($clear);
  1065. }
  1066. $buffer = $this->format_paragraph(array('text' => $para));
  1067. if ($block == 'bq') {
  1068. if (!preg_match('/<p[ >]/', $buffer)) { $post .= '</p>'; }
  1069. if ($sticky == 0) {
  1070. $post .= '</blockquote>';
  1071. }
  1072. } else {
  1073. $post .= '</' . $block . '>';
  1074. }
  1075. if (preg_match('{' . $this->blocktags . '}x', $buffer)) {
  1076. $buffer = preg_replace('/^\n\n/s', '', $buffer, 1);
  1077. $out .= $buffer;
  1078. } else {
  1079. if ($filter) { $buffer = $this->format_block(array('text' => "|$filter|" . $buffer, 'inline' => 1)); }
  1080. $out .= $pre . $buffer . $post;
  1081. }
  1082. }
  1083. if ($sticky) {
  1084. if ($block == 'bc') {
  1085. // close our blockcode section
  1086. $out .= $this->options['_blockcode_close']; // . "\n\n";
  1087. } elseif ($block == 'bq') {
  1088. $out .= '</blockquote>'; // . "\n\n";
  1089. } elseif (($block == 'table') && $stickybuff) {
  1090. $table_out = $this->format_table(array('text' => $stickybuff));
  1091. if ($table_out) { $out .= $table_out; }
  1092. } elseif (($block == 'dl') && $stickybuff) {
  1093. $dl_out = $this->format_deflist(array('text' => $stickybuff));
  1094. if ($dl_out) { $out .= $dl_out; }
  1095. }
  1096. }
  1097. // cleanup-- restore preserved blocks
  1098. for ($i = count($this->repl[0]); $i > 0; $i--) {
  1099. $out = preg_replace('!(?:<|&lt;)textile#' . $i . '(?:>|&gt;)!', str_replace('$', '\\$', $this->repl[0][$i - 1]), $out, 1);
  1100. }
  1101. array_shift($this->repl);
  1102. // scan for br, hr tags that are not closed and close them
  1103. // only for xhtml! just the common ones -- don't fret over input
  1104. // and the like.
  1105. if (preg_match('/^xhtml/i', $this->flavor())) {
  1106. $out = preg_replace('/(<(?:img|br|hr)[^>]*?(?<!\/))>/', '$1 />', $out);
  1107. }
  1108. return $out;
  1109. } // function process
  1110. /**
  1111. * Processes a single paragraph. The following attributes are
  1112. * allowed:
  1113. *
  1114. * <ul>
  1115. *
  1116. * <li><b>text</b>
  1117. *
  1118. * The text to be processed.</li>
  1119. *
  1120. * </ul>
  1121. *
  1122. * @param $args An @c array specifying the attributes for formatting
  1123. * the paragraph.
  1124. *
  1125. * @return A @c string containing the formatted paragraph.
  1126. *
  1127. * @private
  1128. */
  1129. function format_paragraph($args) {
  1130. $buffer = (isset($args['text']) ? $args['text'] : '');
  1131. array_unshift($this->repl, array());
  1132. $buffer = preg_replace_callback('{(?:^|(?<=[\s>])|([{[]))
  1133. ==(.+?)==
  1134. (?:$|([\]}])|(?=' . $this->punct . '{1,2}|\s))}sx',
  1135. $this->_cb('$me->_repl($me->repl[0], $me->format_block(array("text" => $m[2], "inline" => 1, "pre" => $m[1], "post" => $m[3])))'), $buffer);
  1136. unset($tokens);
  1137. if (preg_match('/</', $buffer) && (!$this->disable_html())) { // optimization -- no point in tokenizing if we
  1138. // have no tags to tokenize
  1139. $tokens = $this->_tokenize($buffer);
  1140. } else {
  1141. $tokens = array(array('text', $buffer));
  1142. }
  1143. $result = '';
  1144. foreach ($tokens as $token) {
  1145. $text = $token[1];
  1146. if ($token[0] == 'tag') {
  1147. $text = preg_replace('/&(?!amp;)/', '&amp;', $text);
  1148. $result .= $text;
  1149. } else {
  1150. $text = $this->format_inline(array('text' => $text));
  1151. $result .= $text;
  1152. }
  1153. }
  1154. // now, add line breaks for lines that contain plaintext
  1155. $lines = preg_split('/\n/', $result);
  1156. $result = '';
  1157. $needs_closing = 0;
  1158. foreach ($lines as $line) {
  1159. if (!preg_match('{(' . $this->blocktags . ')}x', $line)
  1160. && ((preg_match('/^[^<]/', $line) || preg_match('/>[^<]/', $line))
  1161. || !preg_match('/<img /', $line))) {
  1162. if ($this->options['_line_open']) {
  1163. if ($result != '') { $result .= "\n"; }
  1164. $result .= $this->options['_line_open'] . $line . $this->options['_line_close'];
  1165. } else {
  1166. if ($needs_closing) {
  1167. $result .= $this->options['_line_close'] . "\n";
  1168. } else {
  1169. $needs_closing = 1;
  1170. if ($result != '') { $result .= "\n"; }
  1171. }
  1172. $result .= $line;
  1173. }
  1174. } else {
  1175. if ($needs_closing) {
  1176. $result .= $this->options['_line_close'] . "\n";
  1177. } else {
  1178. if ($result != '') { $result .= "\n"; }
  1179. }
  1180. $result .= $line;
  1181. $needs_closing = 0;
  1182. }
  1183. }
  1184. // at this point, we will restore the \001's to \n's (reversing
  1185. // the step taken in _tokenize).
  1186. //$result = preg_replace('/\r/', "\n", $result);
  1187. $result = preg_replace('/\001/', "\n", $result);
  1188. for ($i = count($this->repl[0]); $i > 0; $i--) {
  1189. $result = preg_replace("|<textile#$i>|", str_replace('$', '\\$', $this->repl[0][$i - 1]), $result, 1);
  1190. }
  1191. array_shift($this->repl);
  1192. // quotalize
  1193. if ($this->options['do_quotes']) {
  1194. $result = $this->process_quotes($result);
  1195. }
  1196. return $result;
  1197. } // function format_paragraph
  1198. /**
  1199. * Processes an inline string (plaintext) for Textile syntax.
  1200. * The following attributes are allowed:
  1201. *
  1202. * <ul>
  1203. *
  1204. * <li><b>text</b>
  1205. *
  1206. * The text to be processed.</li>
  1207. *
  1208. * </ul>
  1209. *
  1210. * @param $args An @c array specifying the attributes for formatting
  1211. * the inline string.
  1212. *
  1213. * @return A @c string containing the formatted inline string.
  1214. *
  1215. * @private
  1216. */
  1217. function format_inline($args) {
  1218. $qtags = array(array('**', 'b', '(?<!\*)\*\*(?!\*)', '\*'),
  1219. array('__', 'i', '(?<!_)__(?!_)', '_'),
  1220. array('??', 'cite', '\?\?(?!\?)', '\?'),
  1221. array('*', 'strong', '(?<!\*)\*(?!\*)', '\*'),
  1222. array('_', 'em', '(?<!_)_(?!_)', '_'),
  1223. array('-', 'del', '(?<!\-)\-(?!\-)', '-'),
  1224. array('+', 'ins', '(?<!\+)\+(?!\+)', '\+'),
  1225. array('++', 'big', '(?<!\+)\+\+(?!\+)', '\+\+'),
  1226. array('--', 'small', '(?<!\-)\-\-(?!\-)', '\-\-'),
  1227. array('~', 'sub', '(?<!\~)\~(?![\\\\/~])', '\~'));
  1228. $text = (isset($args['text']) ? $args['text'] : '');
  1229. array_unshift($this->repl, array());
  1230. $text = preg_replace_callback('{' . $this->codere . '}mx', $this->_cb('$me->_repl($me->repl[0], $me->format_code(array("text" => $m[2] . $m[4], "lang" => $m[1] . $m[3])))'), $text);
  1231. // images must be processed before encoding the text since they might
  1232. // have the <, > alignment specifiers...
  1233. // !blah (alt)! -> image
  1234. $text = preg_replace_callback('{(?:^|(?<=[\s>])|([{[])) # $1: open brace/bracket
  1235. ! # opening
  1236. (' . $this->imgalignre . '?) # $2: optional alignment
  1237. (' . $this->clstypadre . '*) # $3: optional CSS class/id
  1238. (' . $this->imgalignre . '?) # $4: optional alignment
  1239. (?:\s*) # space between alignment/css stuff
  1240. ([^\s\(!]+) # $5: filename
  1241. (\s*[^\(!]*(?:\([^\)]+\))?[^!]*) # $6: extras (alt text)
  1242. ! # closing
  1243. (?::(\d+|' . $this->urlre . '))? # $7: optional URL
  1244. (?:$|([\]}])|(?=' . $this->punct . '{1,2}|\s)) # $8: closing brace/bracket
  1245. }mx', $this->_cb('$me->_repl($me->repl[0], $me->format_image(array("pre" => $m[1], "src" => $m[5], "align" => ($m[2] ? $m[2] : $m[4]), "extra" => $m[6], "url" => $m[7], "clsty" => $m[3], "post" => $m[8])))'), $text);
  1246. $text = preg_replace_callback('{(?:^|(?<=[\s>])|([{[])) # $1: open brace/bracket
  1247. % # opening
  1248. (' . $this->halignre . '?) # $2: optional alignment
  1249. (' . $this->clstyre . '*) # $3: optional CSS class/id
  1250. (' . $this->halignre . '?) # $4: optional alignment
  1251. (?:\s*) # spacing
  1252. ([^%]+?) # $5: text
  1253. % # closing
  1254. (?::(\d+|' . $this->urlre . '))? # $6: optional URL
  1255. (?:$|([]}])|(?=' . $this->punct . '{1,2}|\s)) # $7: closing brace/bracket
  1256. }mx', $this->_cb('$me->_repl($me->repl[0], $me->format_span(array("pre" => $m[1], "text" => $m[5], "align" => ($m[2] ? $m[2] : $m[4]), "cite" => $m[6], "clsty" => $m[3], "post" => $m[7])))'), $text);
  1257. $text = $this->encode_html($text);
  1258. $text = preg_replace('!&lt;textile#(\d+)&gt;!', '<textile#$1>', $text);
  1259. $text = preg_replace('!&amp;quot;!', '&#34;', $text);
  1260. $text = preg_replace('!&amp;(([a-z]+|#\d+);)!', '&$1', $text);
  1261. $text = preg_replace('!&quot;!', '"', $text);
  1262. // These create markup with entities. Do first and 'save' result for later:
  1263. // "text":url -> hyperlink
  1264. // links with brackets surrounding
  1265. $parenre = '\( (?: [^()] )* \)';
  1266. $text = preg_replace_callback('{(
  1267. [{[]
  1268. (?:
  1269. (?:" # quote character
  1270. (' . $this->clstyre . '*)? # $2: optional CSS class/id
  1271. ([^"]+?) # $3: link text
  1272. (?:\( ( (?:[^()]|' . $parenre . ')*) \))? # $4: optional link title
  1273. " # closing quote
  1274. )
  1275. |
  1276. (?:\' # open single quote
  1277. (' . $this->clstyre . '*)? # $5: optional CSS class/id
  1278. ([^\']+?) # $6: link text
  1279. (?:\( ( (?:[^()]|' . $parenre . ')*) \))? # $7: optional link title
  1280. \' # closing quote
  1281. )
  1282. )
  1283. :(.+?) # $8: URL suffix
  1284. [\]}]
  1285. )
  1286. }mx', $this->_cb('$me->_repl($me->repl[0], $me->format_link(array("text" => $m[1], "linktext" => $m[3] . $m[6], "title" => $me->encode_html_basic($m[4] . $m[7]), "url" => $m[8], "clsty" => $m[2] . $m[5])))'), $text);
  1287. $text = preg_replace_callback('{((?:^|(?<=[\s>\(])) # $1: open brace/bracket
  1288. (?: (?:" # quote character "
  1289. (' . $this->clstyre . '*)? # $2: optional CSS class/id
  1290. ([^"]+?) # $3: link text "
  1291. (?:\( ( (?:[^()]|' . $parenre . ')*) \))? # $4: optional link title
  1292. " # closing quote # "
  1293. )
  1294. |
  1295. (?:\' # open single quote \'
  1296. (' . $this->clstyre . '*)? # $5: optional CSS class/id
  1297. ([^\']+?) # $6: link text \'
  1298. (?:\( ( (?:[^()]|' . $parenre . ')*) \))? # $7: optional link title
  1299. \' # closing quote \'
  1300. )
  1301. )
  1302. :(\d+|' . $this->urlre . ') # $8: URL suffix
  1303. (?:$|(?=' . $this->punct . '{1,2}|\s))) # $9: closing brace/bracket
  1304. }mx', $this->_cb('$me->_repl($me->repl[0], $me->format_link(array("text" => $m[1], "linktext" => $m[3] . $m[6], "title" => $me->encode_html_basic($m[4] . $m[7]), "url" => $m[8], "clsty" => $m[2] . $m[5])))'), $text);
  1305. if (preg_match('/^xhtml2/', $this->flavor())) {
  1306. // citation with cite link
  1307. $text = preg_replace_callback('{(?:^|(?<=[\s>\'"\(])|([{[])) # $1: open brace/bracket \'
  1308. \?\? # opening \'??\'
  1309. ([^\?]+?) # $2: characters (can\'t contain \'?\')
  1310. \?\? # closing \'??\'
  1311. :(\d+|' . $this->urlre . ') # $3: optional citation URL
  1312. (?:$|([\]}])|(?=' . $this->punct . '{1,2}|\s)) # $4: closing brace/bracket
  1313. }mx', $this->_cb('$me->_repl($me->repl[0], $me->format_cite(array("pre" => $m[1], "text" => $m[2], "cite" => $m[3], "post" => $m[4])))'), $text);
  1314. }
  1315. // footnotes
  1316. if (preg_match('/[^ ]\[\d+\]/', $text)) {
  1317. $fntag = '<sup';
  1318. if ($this->options['css']['class_footnote']) { $fntag .= ' class="' . $this->options['css']['class_footnote'] . '"'; }
  1319. $fntag .= '><a href="#' . ($this->options['css']['id_footnote_prefix'] ? $this->options['css']['id_footnote_prefix'] : 'fn');
  1320. $text = preg_replace('|([^ ])\[(\d+)\]|', '$1' . $fntag . '$2">$2</a></sup>', $text);
  1321. }
  1322. // translate macros:
  1323. $text = preg_replace_callback('{(\{)(.+?)(\})}x',
  1324. $this->_cb('$me->format_macro(array("pre" => $m[1], "post" => $m[3], "macro" => $m[2]))'), $text);
  1325. // these were present with textile 1 and are common enough
  1326. // to not require macro braces...
  1327. // (tm) -> &trade;
  1328. $text = preg_replace('|[\(\[]TM[\)\]]|i', '&#8482;', $text);
  1329. // (c) -> &copy;
  1330. $text = preg_replace('|[\(\[]C[\)\]]|i', '&#169;', $text);
  1331. // (r) -> &reg;
  1332. $text = preg_replace('|[\(\[]R[\)\]]|i', '&#174;', $text);
  1333. if ($this->preserve_spaces()) {
  1334. // replace two spaces with an em space
  1335. $text = preg_replace('/(?<!\s)\ \ (?!=\s)/', '&#8195;', $text);
  1336. }
  1337. $redo = preg_match('/[\*_\?\-\+\^\~]/', $text);
  1338. $last = $text;
  1339. while ($redo) {
  1340. // simple replacements...
  1341. $redo = 0;
  1342. foreach ($qtags as $tag) {
  1343. list ($this->tmp['f'][], $this->tmp['r'][], $qf, $cls) = $tag;
  1344. if ($last != ($text = preg_replace_callback('{(?:^|(?<=[\s>\'"])|([{[])) # "\' $1 - pre
  1345. ' . $qf . ' #
  1346. (?:(' . $this->clstyre . '*))? # $2 - attributes
  1347. ([^' . $cls . '\s].*?) # $3 - content
  1348. (?<=\S)' . $qf . ' #
  1349. (?:$|([\]}])|(?=' . $this->punct . '{1,2}|\s)) # $4 - post
  1350. }mx', $this->_cb('$me->format_tag(array("tag" => end($me->tmp["r"]), "marker" => end($me->tmp["f"]), "pre" => $m[1], "text" => $m[3], "clsty" => $m[2], "post" => $m[4]))'), $text))) {
  1351. $redo = ($redo || ($last != $text));
  1352. $last = $text;
  1353. }
  1354. array_pop($this->tmp['f']); array_pop($this->tmp['r']);
  1355. }
  1356. }
  1357. // superscript is an even simpler replacement...
  1358. $text = preg_replace('/(?<!\^)\^(?!\^)(.+?)(?<!\^)\^(?!\^)/', '<sup>$1</sup>', $text);
  1359. // ABC(Aye Bee Cee) -> acronym
  1360. $text = preg_replace_callback('{\b([A-Z][A-Za-z0-9]*?[A-Z0-9]+?)\b(?:[(]([^)]*)[)])}',
  1361. $this->_cb('$me->_repl($me->repl[0],"<acronym title=\"" . $me->encode_html_basic($m[2]) . "\">$m[1]</acronym>")'), $text);
  1362. // ABC -> 'capped' span
  1363. if ($this->tmp['caps'][] = $this->options['css']['class_caps']) {
  1364. $text = preg_replace_callback('/(^|[^"][>\s]) # "
  1365. ((?:[A-Z](?:[A-Z0-9\.,\']|\&amp;){2,}\ *)+?) # \'
  1366. (?=[^A-Z\.0-9]|$)
  1367. /mx', $this->_cb('$m[1] . $me->_repl($me->repl[0], "<span class=\"" . end($me->tmp["caps"]) . "\">$m[2]</span>")'), $text);
  1368. }
  1369. array_pop($this->tmp['caps']);
  1370. // nxn -> n&times;n
  1371. $text = preg_replace('!((?:[0-9\.]0|[1-9]|\d[\'"])\ ?)x(\ ?\d)!', '$1&#215;$2', $text);
  1372. // translate these entities to the Unicode equivalents:
  1373. $text = preg_replace('/&#133;/', '&#8230;', $text);
  1374. $text = preg_replace('/&#145;/', '&#8216;', $text);
  1375. $text = preg_replace('/&#146;/', '&#8217;', $text);
  1376. $text = preg_replace('/&#147;/', '&#8220;', $text);
  1377. $text = preg_replace('/&#148;/', '&#8221;', $text);
  1378. $text = preg_replace('/&#150;/', '&#8211;', $text);
  1379. $text = preg_replace('/&#151;/', '&#8212;', $text);
  1380. // Restore replacements done earlier:
  1381. for ($i = count($this->repl[0]); $i > 0; $i--) {
  1382. $text = preg_replace("|<textile#$i>|", str_replace('$', '\\$', $this->repl[0][$i - 1]), $text);
  1383. }
  1384. array_shift($this->repl);
  1385. // translate entities to characters for highbit stuff since
  1386. // we're using utf8
  1387. // removed for backward compatability with older versions of Perl
  1388. //if (preg_match('/^utf-?8$/i', $this->options['charset'])) {
  1389. // // translate any unicode entities to native UTF-8
  1390. // $text = preg_replace('/\&\#(\d+);/e', '($1 > 127) ? pack('U', $1) : chr($1)', $text);
  1391. //}
  1392. return $text;
  1393. } // function format_inline
  1394. /**
  1395. * Responsible for processing a particular macro. Arguments passed
  1396. * include:
  1397. *
  1398. * <ul>
  1399. *
  1400. * <li><b>pre</b>
  1401. *
  1402. * open brace character</li>
  1403. *
  1404. * <li><b>post</b>
  1405. *
  1406. * close brace character</li>
  1407. *
  1408. * <li><b>macro</b>
  1409. *
  1410. * the macro to be executed</li>
  1411. *
  1412. * </ul>
  1413. *
  1414. * The return value from this method would be the replacement
  1415. * text for the macro given. If the macro is not defined, it will
  1416. * return pre + macro + post, thereby preserving the original
  1417. * macro string.
  1418. *
  1419. * @param $attrs An @c array containing the attributes for
  1420. * formatting the macro.
  1421. *
  1422. * @return A @c string containing the formatted macro.
  1423. *
  1424. * @private
  1425. */
  1426. function format_macro($attrs) {
  1427. $macro = $attrs['macro'];
  1428. if ($this->options['macros'][$macro]) {
  1429. return $this->options['macros'][$macro];
  1430. }
  1431. return $attrs['pre'] . $macro . $attrs['post'];
  1432. } // function format_macro
  1433. /**
  1434. * Processes text for a citation tag. The following attributes
  1435. * are allowed:
  1436. *
  1437. * <ul>
  1438. *
  1439. * <li><b>pre</b>
  1440. *
  1441. * Any text that comes before the citation.</li>
  1442. *
  1443. * <li><b>text</b>
  1444. *
  1445. * The text that is being cited.</li>
  1446. *
  1447. * <li><b>cite</b>
  1448. *
  1449. * The URL of the citation.</li>
  1450. *
  1451. * <li><b>post</b>
  1452. *
  1453. * Any text that follows the citation.</li>
  1454. *
  1455. * </ul>
  1456. *
  1457. * @param $args An @c array specifying the attributes for formatting
  1458. * the citation.
  1459. *
  1460. * @return A @c string containing the formatted citation.
  1461. *
  1462. * @private
  1463. */
  1464. function format_cite($args) {
  1465. $pre = (isset($args['pre']) ? $args['pre'] : '');
  1466. $text = (isset($args['text']) ? $args['text'] : '');
  1467. $cite = $args['cite'];
  1468. $post = (isset($args['post']) ? $args['post'] : '');
  1469. $this->_strip_borders($pre, $post);
  1470. $tag = $pre . '<cite';
  1471. if (preg_match('/^xhtml2/', $this->flavor()) && $cite) {
  1472. $cite = $this->format_url(array('url' => $cite));
  1473. $tag .= " cite=\"$cite\"";
  1474. } else {
  1475. $post .= ':';
  1476. }
  1477. $tag .= '>';
  1478. return $tag . $this->format_inline(array('text' => $text)) . '</cite>' . $post;
  1479. } // function format_cite
  1480. /**
  1481. * Processes '@...@' type blocks (code snippets). The following
  1482. * attributes are allowed:
  1483. *
  1484. * <ul>
  1485. *
  1486. * <li><b>text</b>
  1487. *
  1488. * The text of the code itself.</li>
  1489. *
  1490. * <li><b>lang</b>
  1491. *
  1492. * The language (programming language) for the code.</li>
  1493. *
  1494. * </ul>
  1495. *
  1496. * @param $args An @c array specifying the attributes for formatting
  1497. * the code.
  1498. *
  1499. * @return A @c string containing the formatted code.
  1500. *
  1501. * @private
  1502. */
  1503. function format_code($args) {
  1504. $code = (isset($args['text']) ? $args['text'] : '');
  1505. $lang = $args['lang'];
  1506. $code = $this->encode_html($code, 1);
  1507. $code = preg_replace('/&lt;textile#(\d+)&gt;/', '<textile#$1>', $code);
  1508. $tag = '<code';
  1509. if ($lang) { $tag .= " language=\"$lang\""; }
  1510. return $tag . '>' . $code . '</code>';
  1511. } // function format_code
  1512. /**
  1513. * Returns a string of tag attributes to accomodate the class,
  1514. * style and symbols present in @c $clsty.
  1515. *
  1516. * @c $clsty is checked for:
  1517. *
  1518. * <ul>
  1519. *
  1520. * <li><b><code>{...}</code></b>
  1521. *
  1522. * style rules. If present, they are appended to <code>$style</code>.</li>
  1523. *
  1524. * <li><b><code>(...#...)</code></b>
  1525. *
  1526. * class and/or ID name declaration</li>
  1527. *
  1528. * <li><b><code>(</code> (one or more)</b>
  1529. *
  1530. * pad left characters</li>
  1531. *
  1532. * <li><b><code>)</code> (one or more)</b>
  1533. *
  1534. * pad right characters</li>
  1535. *
  1536. * <li><b><code>[ll]</code></b>
  1537. *
  1538. * language declaration</li>
  1539. *
  1540. * </ul>
  1541. *
  1542. * The attribute string returned will contain any combination
  1543. * of class, id, style and/or lang attributes.
  1544. *
  1545. * @param $clsty A @c string specifying the class/style to process.
  1546. * @param $class A @c string specifying the predetermined class.
  1547. * @param $style A @c string specifying the predetermined style.
  1548. *
  1549. * @return A @c string containing the formatted class, ID, style,
  1550. * and/or language.
  1551. *
  1552. * @private
  1553. */
  1554. function format_classstyle($clsty = NULL, $class = NULL, $style = NULL) {
  1555. $class = preg_replace('/^ /', '', $class, 1);
  1556. unset($lang, $padleft, $padright, $id);
  1557. if ($clsty && preg_match('/{([^}]+)}/', $clsty, $matches)) {
  1558. $_style = $matches[1];
  1559. $_style = preg_replace('/\n/', ' ', $_style);
  1560. $style .= ';' . $_style;
  1561. $clsty = preg_replace('/{[^}]+}/', '', $clsty);
  1562. }
  1563. if ($clsty && (preg_match('/\(([A-Za-z0-9_\- ]+?)(?:#(.+?))?\)/', $clsty, $matches) ||
  1564. preg_match('/\(([A-Za-z0-9_\- ]+?)?(?:#(.+?))\)/', $clsty, $matches))) {
  1565. if ($matches[1] || $matches[2]) {
  1566. if ($class) {
  1567. $class = $matches[1] . ' ' . $class;
  1568. } else {
  1569. $class = $matches[1];
  1570. }
  1571. $id = $matches[2];
  1572. if ($class) {
  1573. $clsty = preg_replace('/\([A-Za-z0-9_\- ]+?(#.*?)?\)/', '', $clsty);
  1574. }
  1575. if ($id) {
  1576. $clsty = preg_replace('/\(#.+?\)/', '', $clsty);
  1577. }
  1578. }
  1579. }
  1580. if ($clsty && preg_match('/(\(+)/', $clsty, $matches)) {
  1581. $padleft = strlen($matches[1]);
  1582. $clsty = preg_replace('/\(+/', '', $clsty, 1);
  1583. }
  1584. if ($clsty && preg_match('/(\)+)/', $clsty, $matches)) {
  1585. $padright = strlen($matches[1]);
  1586. $clsty = preg_replace('/\)+/', '', $clsty, 1);
  1587. }
  1588. if ($clsty && preg_match('/\[(.+?)\]/', $clsty, $matches)) {
  1589. $lang = $matches[1];
  1590. $clsty = preg_replace('/\[.+?\]/', '', $clsty);
  1591. }
  1592. $attrs = '';
  1593. if ($padleft) { $style .= ";padding-left:${padleft}em"; }
  1594. if ($padright) { $style .= ";padding-right:${padright}em"; }
  1595. $style = preg_replace('/^;/', '', $style, 1);
  1596. $class = preg_replace('/^ /', '', $class, 1);
  1597. $class = preg_replace('/ $/', '', $class, 1);
  1598. if ($class) { $attrs .= " class=\"$class\""; }
  1599. if ($id) { $attrs .= " id=\"$id\""; }
  1600. if ($style) { $attrs .= " style=\"$style\""; }
  1601. if ($lang) { $attrs .= " lang=\"$lang\""; }
  1602. $attrs = preg_replace('/^ /', '', $attrs, 1);
  1603. return $attrs;
  1604. } // function format_classstyle
  1605. /**
  1606. * Constructs an HTML tag. Accepted arguments:
  1607. *
  1608. * <ul>
  1609. *
  1610. * <li><b>tag</b>
  1611. *
  1612. * the tag to produce</li>
  1613. *
  1614. * <li><b>text</b>
  1615. *
  1616. * the text to output inside the tag</li>
  1617. *
  1618. * <li><b>pre</b>
  1619. *
  1620. * text to produce before the tag</li>
  1621. *
  1622. * <li><b>post</b>
  1623. *
  1624. * text to produce following the tag</li>
  1625. *
  1626. * <li><b>clsty</b>
  1627. *
  1628. * class and/or style attributes that should be assigned to the tag.</li>
  1629. *
  1630. * </ul>
  1631. *
  1632. * @param $args @c array specifying the attributes for formatting
  1633. * the tag.
  1634. *
  1635. * @return A @c string containing the formatted tag.
  1636. *
  1637. * @private
  1638. */
  1639. function format_tag($args) {
  1640. $tagname = $args['tag'];
  1641. $text = (isset($args['text']) ? $args['text'] : '');
  1642. $pre = (isset($args['pre']) ? $args['pre'] : '');
  1643. $post = (isset($args['post']) ? $args['post'] : '');
  1644. $clsty = (isset($args['clsty']) ? $args['clsty'] : '');
  1645. $this->_strip_borders($pre, $post);
  1646. $tag = "<$tagname";
  1647. $attr = $this->format_classstyle($clsty);
  1648. if ($attr) { $tag .= " $attr"; }
  1649. $tag .= ">$text</$tagname>";
  1650. return $pre . $tag . $post;
  1651. } // function format_tag
  1652. /**
  1653. * Takes a Textile formatted definition list and
  1654. * returns the markup for it. Arguments accepted:
  1655. *
  1656. * <ul>
  1657. *
  1658. * <li><b>text</b>
  1659. *
  1660. * The text to be processed.</li>
  1661. *
  1662. * </ul>
  1663. *
  1664. * @param $args An @c array specifying the attributes for formatting
  1665. * the definition list.
  1666. *
  1667. * @return A @c string containing the formatted definition list.
  1668. *
  1669. * @private
  1670. */
  1671. function format_deflist($args) {
  1672. $str = (isset($args['text']) ? $args['text'] : '');
  1673. unset($clsty);
  1674. $lines = preg_split('/\n/', $str);
  1675. if (preg_match('{^(dl(' . $this->clstyre . '*?)\.\.?(?:\ +|$))}x', $lines[0], $matches)) {
  1676. $clsty = $matches[2];
  1677. $lines[0] = substr($lines[0], strlen($matches[1]));
  1678. }
  1679. unset($dt, $dd);
  1680. $out = '';
  1681. foreach ($lines as $line) {
  1682. if (preg_match('{^((?:' . $this->clstyre . '*)(?:[^\ ].*?)(?<!["\'\ ])):([^\ \/].*)$}x', $line, $matches)) {
  1683. if ($dt && $dd) { $out .= $this->add_term($dt, $dd); }
  1684. $dt = $matches[1];
  1685. $dd = $matches[2];
  1686. } else {
  1687. $dd .= "\n" . $line;
  1688. }
  1689. }
  1690. if ($dt && $dd) { $out .= $this->add_term($dt, $dd); }
  1691. $tag = '<dl';
  1692. if ($clsty) { $attr = $this->format_classstyle($clsty); }
  1693. if ($attr) { $tag .= " $attr"; }
  1694. $tag .= '>' . "\n";
  1695. return $tag . $out . "</dl>\n";
  1696. } // function format_deflist
  1697. /**
  1698. * Processes a single definition list item from the provided term
  1699. * and definition.
  1700. *
  1701. * @param $dt A @c string specifying the term to be defined.
  1702. * @param $dd A @c string specifying the definition for the term.
  1703. *
  1704. * @return A @c string containing the formatted definition list
  1705. * item.
  1706. *
  1707. * @private
  1708. */
  1709. function add_term($dt, $dd) {
  1710. unset($dtattr, $ddattr);
  1711. unset($dtlang);
  1712. if (preg_match('{^(' . $this->clstyre . '*)}x', $dt, $matches)) {
  1713. $param = $matches[1];
  1714. $dtattr = $this->format_classstyle($param);
  1715. if (preg_match('/\[([A-Za-z]+?)\]/', $param, $matches)) {
  1716. $dtlang = $matches[1];
  1717. }
  1718. $dt = substr($dt, strlen($param));
  1719. }
  1720. if (preg_match('{^(' . $this->clstyre . '*)}x', $dd, $matches)) {
  1721. $param = $matches[1];
  1722. // if the language was specified for the term,
  1723. // then apply it to the definition as well (unless
  1724. // already specified of course)
  1725. if ($dtlang && preg_match('/\[([A-Za-z]+?)\]/', $param)) {
  1726. unset($dtlang);
  1727. }
  1728. $ddattr = $this->format_classstyle(($dtlang ? "[$dtlang]" : '') . $param);
  1729. $dd = substr($dd, strlen($param));
  1730. }
  1731. $out = '<dt';
  1732. if ($dtattr) { $out .= " $dtattr"; }
  1733. $out .= '>' . $this->format_inline(array('text' => $dt)) . '</dt>' . "\n";
  1734. if (preg_match('/\n\n/', $dd)) {
  1735. if (preg_match('/\n\n/', $dd)) { $dd = $this->process($dd); }
  1736. } else {
  1737. $dd = $this->format_paragraph(array('text' => $dd));
  1738. }
  1739. $out .= '<dd';
  1740. if ($ddattr) { $out .= " $ddattr"; }
  1741. $out .= '>' . $dd . '</dd>' . "\n";
  1742. return $out;
  1743. } // function add_term
  1744. /**
  1745. * Takes a Textile formatted list (numeric or bulleted) and
  1746. * returns the markup for it. Text that is passed in requires
  1747. * substantial parsing, so the @c format_list method is a little
  1748. * involved. But it should always produce a proper ordered
  1749. * or unordered list. If it cannot (due to misbalanced input),
  1750. * it will return the original text. Arguments accepted:
  1751. *
  1752. * <ul>
  1753. *
  1754. * <li><b>text</b>
  1755. *
  1756. * The text to be processed.</li>
  1757. *
  1758. * </ul>
  1759. *
  1760. * @param $args An @c array specifying the attributes for formatting
  1761. * the list.
  1762. *
  1763. * @return A @c string containing the formatted list.
  1764. *
  1765. * @private
  1766. */
  1767. function format_list($args) {
  1768. $str = (isset($args['text']) ? $args['text'] : '');
  1769. $list_tags = array('*' => 'ul', '#' => 'ol');
  1770. $lines = preg_split('/\n/', $str);
  1771. unset($stack);
  1772. $last_depth = 0;
  1773. $item = '';
  1774. $out = '';
  1775. foreach ($lines as $line) {
  1776. if (preg_match('{^((?:' . $this->clstypadre . '*|' . $this->halignre . ')*)
  1777. ([\#\*]+)
  1778. ((?:' . $this->halignre . '|' . $this->clstypadre . '*)*)
  1779. \ (.+)$}x', $line, $matches)) {
  1780. if ($item != '') {
  1781. if (preg_match('/\n/', $item)) {
  1782. if ($this->options['_line_open']) {
  1783. $item = preg_replace('/(<li[^>]*>|^)/m', '$1' . $this->options['_line_open'], $item);
  1784. $item = preg_replace('/(\n|$)/s', $this->options['_line_close'] . '$1', $item);
  1785. } else {
  1786. $item = preg_replace('/(\n)/s', $this->options['_line_close'] . '$1', $item);
  1787. }
  1788. }
  1789. $out .= $item;
  1790. $item = '';
  1791. }
  1792. $type = substr($matches[2], 0, 1);
  1793. $depth = strlen($matches[2]);
  1794. $blockparam = $matches[1];
  1795. $itemparam = $matches[3];
  1796. $line = $matches[4];
  1797. unset ($blockclsty, $blockalign, $blockattr, $itemattr, $itemclsty,
  1798. $itemalign);
  1799. if (preg_match('{(' . $this->clstypadre . '+)}x', $blockparam, $matches)) {
  1800. $blockclsty = $matches[1];
  1801. }
  1802. if (preg_match('{(' . $this->halignre . '+)}', $blockparam, $matches)) {
  1803. $blockalign = $matches[1];
  1804. }
  1805. if (preg_match('{(' . $this->clstypadre . '+)}x', $itemparam, $matches)) {
  1806. $itemclsty = $matches[1];
  1807. }
  1808. if (preg_match('{(' . $this->halignre . '+)}', $itemparam, $matches)) {
  1809. $itemalign = $matches[1];
  1810. }
  1811. if ($itemclsty) { $itemattr = $this->format_classstyle($itemclsty); }
  1812. if ($depth > $last_depth) {
  1813. for ($j = $last_depth; $j < $depth; $j++) {
  1814. $out .= "\n<$list_tags[$type]";
  1815. $stack[] = $type;
  1816. if ($blockclsty) {
  1817. $blockattr = $this->format_classstyle($blockclsty);
  1818. if ($blockattr) { $out .= ' ' . $blockattr; }
  1819. }
  1820. $out .= ">\n<li";
  1821. if ($itemattr) { $out .= " $itemattr"; }
  1822. $out .= ">";
  1823. }
  1824. } elseif ($depth < $last_depth) {
  1825. for ($j = $depth; $j < $last_depth; $j++) {
  1826. if ($j == $depth) { $out .= "</li>\n"; }
  1827. $type = array_pop($stack);
  1828. $out .= "</$list_tags[$type]>\n</li>\n";
  1829. }
  1830. if ($depth) {
  1831. $out .= '<li';
  1832. if ($itemattr) { $out .= " $itemattr"; }
  1833. $out .= '>';
  1834. }
  1835. } else {
  1836. $out .= "</li>\n<li";
  1837. if ($itemattr) { $out .= " $itemattr"; }
  1838. $out .= '>';
  1839. }
  1840. $last_depth = $depth;
  1841. }
  1842. if ($item != '') { $item .= "\n"; }
  1843. $item .= $this->format_paragraph(array('text' => $line));
  1844. }
  1845. if (preg_match('/\n/', $item, $matches)) {
  1846. if ($this->options['_line_open']) {
  1847. $item = preg_replace('/(<li[^>]*>|^)/m', '$1' . $this->options['_line_open'], $item);
  1848. $item = preg_replace('/(\n|$)/s', $this->options['_line_close'] . '$1', $item);
  1849. } else {
  1850. $item = preg_replace('/(\n)/s', $this->options['_line_close'] . '$1', $item);
  1851. }
  1852. }
  1853. $out .= $item;
  1854. for ($j = 1; $j <= $last_depth; $j++) {
  1855. if ($j == 1) { $out .= '</li>'; }
  1856. $type = array_pop($stack);
  1857. $out .= "\n" . '</' . $list_tags[$type] . '>' . "\n";
  1858. if ($j != $last_depth) { $out .= '</li>'; }
  1859. }
  1860. return $out . "\n";
  1861. } // function format_list
  1862. /**
  1863. * Processes '==xxxxx==' type blocks for filters. A filter
  1864. * would follow the open '==' sequence and is specified within
  1865. * pipe characters, like so:
  1866. * <pre>
  1867. * ==|filter|text to be filtered==
  1868. * </pre>
  1869. * You may specify multiple filters in the filter portion of
  1870. * the string. Simply comma delimit the filters you desire
  1871. * to execute. Filters are defined using the filters method.
  1872. *
  1873. * @param $args An @c array specifying the attributes for formatting
  1874. * the block.
  1875. *
  1876. * @return A @c string containing the formatted block.
  1877. *
  1878. * @private
  1879. */
  1880. function format_block($args) {
  1881. $str = (isset($args['text']) ? $args['text'] : '');
  1882. $inline = $args['inline'];
  1883. $pre = (isset($args['pre']) ? $args['pre'] : '');
  1884. $post = (isset($args['post']) ? $args['post'] : '');
  1885. $this->_strip_borders($pre, $post);
  1886. $filters = (preg_match('/^(\|(?:(?:[a-z0-9_\-]+)\|)+)/', $str, $matches) ? $matches[1] : '');
  1887. if ($filters) {
  1888. $filtreg = preg_replace('/[^A-Za-z0-9]/', '\\\\$1', $filters);
  1889. $str = preg_replace('/^' . $filtreg . '/', '', $str, 1);
  1890. $filters = preg_replace('/^\|/', '', $filters, 1);
  1891. $filters = preg_replace('/\|$/', '', $filter, 1);
  1892. $filters = preg_split('/\|/', $filters);
  1893. $str = $this->apply_filters(array('text' => $str, 'filters' => $filters));
  1894. $count = count($filters);
  1895. if ($str = preg_replace('!(<p>){' . $count . '}!se', '(++$i ? "$1" : "$1")', $str) && $i) {
  1896. $str = preg_replace('!(</p>){' . $count . '}!s', '$1', $str);
  1897. $str = preg_replace('!(<br( /)?>){' . $count . '}!s', '$1', $str);
  1898. }
  1899. }
  1900. if ($inline) {
  1901. // strip off opening para, closing para, since we're
  1902. // operating within an inline block
  1903. $str = preg_replace('/^\s*<p[^>]*>/', '', $str, 1);
  1904. $str = preg_replace('/<\/p>\s*$/', '', $str, 1);
  1905. }
  1906. return $pre . $str . $post;
  1907. } // function format_block
  1908. /**
  1909. * Takes the Textile link attributes and transforms them into
  1910. * a hyperlink.
  1911. *
  1912. * @param $args An @c array specifying the attributes for formatting
  1913. * the link.
  1914. *
  1915. * @return A @c string containing the formatted link.
  1916. *
  1917. * @private
  1918. */
  1919. function format_link($args) {
  1920. $text = (isset($args['text']) ? $args['text'] : '');
  1921. $linktext = (isset($args['linktext']) ? $args['linktext'] : '');
  1922. $title = $args['title'];
  1923. $url = $args['url'];
  1924. $clsty = $args['clsty'];
  1925. if (!$url || ($url == '')) {
  1926. return $text;
  1927. }
  1928. if (isset($this->links) && isset($this->links[$url])) {
  1929. $title = ($title ? $title : $this->links[$url]['title']);
  1930. $url = $this->links[$url]['url'];
  1931. }
  1932. $linktext = preg_replace('/ +$/', '', $linktext, 1);
  1933. $linktext = $this->format_paragraph(array('text' => $linktext));
  1934. $url = $this->format_url(array('linktext' => $linktext, 'url' => $url));
  1935. $tag = "<a href=\"$url\"";
  1936. $attr = $this->format_classstyle($clsty);
  1937. if ($attr) { $tag .= " $attr"; }
  1938. if ($title) {
  1939. $title = preg_replace('/^\s+/', '', $title, 1);
  1940. if (strlen($title)) { $tag .= " title=\"$title\""; }
  1941. }
  1942. $tag .= ">$linktext</a>";
  1943. return $tag;
  1944. } // function format_link
  1945. /**
  1946. * Takes the given @c $url and transforms it appropriately.
  1947. *
  1948. * @param $args An @c array specifying the attributes for formatting
  1949. * the url.
  1950. *
  1951. * @return A @c string containing the formatted url.
  1952. *
  1953. * @private
  1954. */
  1955. function format_url($args) {
  1956. $url = ($args['url'] ? $args['url'] : '');
  1957. if (preg_match('/^(mailto:)?([-\+\w]+@[-\w]+(\.\w[-\w]*)+)$/', $url, $matches)) {
  1958. $url = 'mailto:' . $this->mail_encode($matches[2]);
  1959. }
  1960. if (!preg_match('!^(/|\./|\.\./|#)!', $url)) {
  1961. if (!preg_match('!^(https?|ftp|mailto|nntp|telnet)!', $url)) { $url = "http://$url"; }
  1962. }
  1963. $url = preg_replace('/&(?!amp;)/', '&amp;', $url);
  1964. $url = preg_replace('/\ /', '+', $url);
  1965. $url = preg_replace_callback('/^((?:.+?)\?)(.+)$/', $this->_cb('$m[1] . $me->encode_url($m[2])'), $url);
  1966. return $url;
  1967. } // function format_url
  1968. /**
  1969. * Takes a Textile formatted span and returns the markup for it.
  1970. *
  1971. * @return A @c string containing the formatted span.
  1972. *
  1973. * @private
  1974. */
  1975. function format_span($args) {
  1976. $text = (isset($args['text']) ? $args['text'] : '');
  1977. $pre = (isset($args['pre']) ? $args['pre'] : '');
  1978. $post = (isset($args['post']) ? $args['post'] : '');
  1979. $align = $args['align'];
  1980. $cite = (isset($args['cite']) ? $args['cite'] : '');
  1981. $clsty = $args['clsty'];
  1982. $this->_strip_borders($pre, $post);
  1983. unset($class, $style);
  1984. $tag = "<span";
  1985. $style = '';
  1986. if ($align) {
  1987. if ($self->options['css_mode']) {
  1988. $alignment = $this->_halign($align);
  1989. if ($alignment) { $style .= ";float:$alignment"; }
  1990. if ($alignment) { $class .= ' ' . $this->options['css']["class_align_$alignment"]; }
  1991. } else {
  1992. $alignment = ($this->_halign($align) ? $this->_halign($align) : $this->_valign($align));
  1993. if ($alignment) { $tag .= " align=\"$alignment\""; }
  1994. }
  1995. }
  1996. $attr = $this->format_classstyle($clsty, $class, $style);
  1997. if ($attr) { $tag .= " $attr"; }
  1998. if ($cite) {
  1999. $cite = preg_replace('/^:/', '', $cite, 1);
  2000. $cite = $this->format_url(array('url' => $cite));
  2001. $tag .= " cite=\"$cite\"";
  2002. }
  2003. return $pre . $tag . '>' . $this->format_paragraph(array('text' => $text)) . '</span>' . $post;
  2004. } // function format_span
  2005. /**
  2006. * Returns markup for the given image. @c $src is the location of
  2007. * the image, @c $extra contains the optional height/width and/or
  2008. * alt text. @c $url is an optional hyperlink for the image. @c $class
  2009. * holds the optional CSS class attribute.
  2010. *
  2011. * Arguments you may pass:
  2012. *
  2013. * <ul>
  2014. *
  2015. * <li><b>src</b>
  2016. *
  2017. * The 'src' (URL) for the image. This may be a local path,
  2018. * ideally starting with a '/'. Images can be located within
  2019. * the file system if the docroot method is used to specify
  2020. * where the docroot resides. If the image can be found, the
  2021. * image_size method is used to determine the dimensions of
  2022. * the image.</li>
  2023. *
  2024. * <li><b>extra</b>
  2025. *
  2026. * Additional parameters for the image. This would include
  2027. * alt text, height/width specification or scaling instructions.</li>
  2028. *
  2029. * <li><b>align</b>
  2030. *
  2031. * Alignment attribute.</li>
  2032. *
  2033. * <li><b>pre</b>
  2034. *
  2035. * Text to produce prior to the tag.</li>
  2036. *
  2037. * <li><b>post</b>
  2038. *
  2039. * Text to produce following the tag.</li>
  2040. *
  2041. * <li><b>link</b>
  2042. *
  2043. * Optional URL to connect with the image tag.</li>
  2044. *
  2045. * <li><b>clsty</b>
  2046. *
  2047. * Class and/or style attributes.</li>
  2048. *
  2049. * </ul>
  2050. *
  2051. * @param $args An @c array specifying the attributes for formatting
  2052. * the image.
  2053. *
  2054. * @return A @c string containing the formatted image.
  2055. *
  2056. * @private
  2057. */
  2058. function format_image($args) {
  2059. $src = (isset($args['src']) ? $args['src'] : '');
  2060. $extra = $args['extra'];
  2061. $align = $args['align'];
  2062. $pre = (isset($args['pre']) ? $args['pre'] : '');
  2063. $post = (isset($args['post']) ? $args['post'] : '');
  2064. $link = $args['url'];
  2065. $clsty = $args['clsty'];
  2066. $this->_strip_borders($pre, $post);
  2067. if (strlen($src) == 0) { return $pre . '!!' . $post; }
  2068. unset($tag);
  2069. if (preg_match('/^xhtml2/', $this->options['flavor'])) {
  2070. unset($type); // poor man's mime typing. need to extend this externally
  2071. if (preg_match('/(?:\.jpeg|\.jpg)$/i', $src)) {
  2072. $type = 'image/jpeg';
  2073. } elseif (preg_match('/\.gif$/i', $src)) {
  2074. $type = 'image/gif';
  2075. } elseif (preg_match('/\.png$/i', $src)) {
  2076. $type = 'image/png';
  2077. } elseif (preg_match('/\.tiff$/i', $src)) {
  2078. $type = 'image/tiff';
  2079. }
  2080. $tag = "<object";
  2081. if ($type) { $tag .= " type=\"$type\""; }
  2082. $tag .= " data=\"$src\"";
  2083. } else {
  2084. $tag = "<img src=\"$src\"";
  2085. }
  2086. unset($class, $style);
  2087. if ($align) {
  2088. if ($this->options['css_mode']) {
  2089. $alignment = $this->_halign($align);
  2090. if ($alignment) { $style .= ";float:$alignment"; }
  2091. if ($alignment) { $class .= ' ' . $alignment; }
  2092. $alignment = $this->_valign($align);
  2093. if ($alignment) {
  2094. $imgvalign = (preg_match('/(top|bottom)/', $alignment) ? 'text-' . $alignment : $alignment);
  2095. if ($imgvalign) { $style .= ";vertical-align:$imgvalign"; }
  2096. if ($alignment) { $class .= ' ' . $this->options['css']["class_align_$alignment"]; }
  2097. }
  2098. } else {
  2099. $alignment = ($this->_halign($align) ? $this->_halign($align) : $this->_valign($align));
  2100. if ($alignment) { $tag .= " align=\"$alignment\""; }
  2101. }
  2102. }
  2103. unset($pctw, $pcth, $w, $h, $alt);
  2104. if ($extra) {
  2105. $alt = (preg_match('/\(([^\)]+)\)/', $extra, $matches) ? $matches[1] : '');
  2106. $extra = preg_replace('/\([^\)]+\)/', '', $extra, 1);
  2107. $pct = (preg_match('/(^|\s)(\d+)%(\s|$)/', $extra, $matches) ? $matches[2] : '');
  2108. if (!$pct) {
  2109. list($pctw, $pcth) = (preg_match('/(^|\s)(\d+)%x(\d+)%(\s|$)/', $extra, $matches) ? array($matches[2], $matches[3]) : NULL);
  2110. } else {
  2111. $pctw = $pcth = $pct;
  2112. }
  2113. if (!$pctw && !$pcth) {
  2114. list($w,$h) = (preg_match('/(^|\s)(\d+|\*)x(\d+|\*)(\s|$)/', $extra, $matches) ? array($matches[2], $matches[3]) : NULL);
  2115. if ($w == '*') { $w = ''; }
  2116. if ($h == '*') { $h = ''; }
  2117. if (!$w) {
  2118. $w = (preg_match('/(^|[,\s])(\d+)w([\s,]|$)/', $extra, $matches) ? $matches[2] : '');
  2119. }
  2120. if (!$h) {
  2121. $h = (preg_match('/(^|[,\s])(\d+)h([\s,]|$)/', $extra, $matches) ? $matches[2] : '');
  2122. }
  2123. }
  2124. }
  2125. $alt = ($alt ? $alt : '');
  2126. if (!preg_match('/^xhtml2/', $this->options['flavor'])) {
  2127. $tag .= ' alt="' . $this->encode_html_basic($alt) . '"';
  2128. }
  2129. if ($w && $h) {
  2130. if (!preg_match('/^xhtml2/', $this->options['flavor'])) {
  2131. $tag .= " height=\"$h\" width=\"$w\"";
  2132. } else {
  2133. $style .= ";height:${h}px;width:${w}px";
  2134. }
  2135. } else {
  2136. list($image_w, $image_h) = $this->image_size($src);
  2137. if (($image_w && $image_h) && ($w || $h)) {
  2138. // image size determined, but only width or height specified
  2139. if ($w && !$h) {
  2140. // width defined, scale down height proportionately
  2141. $h = intval($image_h * ($w / $image_w));
  2142. } elseif ($h && !$w) {
  2143. $w = intval($image_w * ($h / $image_h));
  2144. }
  2145. } else {
  2146. $w = $image_w;
  2147. $h = $image_h;
  2148. }
  2149. if ($w && $h) {
  2150. if ($pctw || $pcth) {
  2151. $w = intval($w * $pctw / 100);
  2152. $h = intval($h * $pcth / 100);
  2153. }
  2154. if (!preg_match('/^xhtml2/', $this->options['flavor'])) {
  2155. $tag .= " height=\"$h\" width=\"$w\"";
  2156. } else {
  2157. $style .= ";height:{$h}px;width:{$w}px";
  2158. }
  2159. }
  2160. }
  2161. $attr = $this->format_classstyle($clsty, $class, $style);
  2162. if ($attr) { $tag .= " $attr"; }
  2163. if (preg_match('/^xhtml2/', $this->options['flavor'])) {
  2164. $tag .= '><p>' . $this->encode_html_basic($alt) . '</p></object>';
  2165. } elseif (preg_match('/^xhtml/', $this->options['flavor'])) {
  2166. $tag .= ' />';
  2167. } else {
  2168. $tag .= '>';
  2169. }
  2170. if ($link) {
  2171. $link = preg_replace('/^:/', '', $link, 1);
  2172. $link = $this->format_url(array('url' => $link));
  2173. $tag = '<a href="' . $link . '">' . $tag . '</a>';
  2174. }
  2175. return $pre . $tag . $post;
  2176. } // function format_image
  2177. /**
  2178. * Takes a Wiki-ish string of data and transforms it into a full
  2179. * table.
  2180. *
  2181. * @param $args An @c array specifying the attributes for formatting
  2182. * the table.
  2183. *
  2184. * @return A @c string containing the formatted table.
  2185. *
  2186. * @private
  2187. */
  2188. function format_table($args) {
  2189. $str = (isset($args['text']) ? $args['text'] : '');
  2190. $lines = preg_split('/\n/', $str);
  2191. unset($rows);
  2192. $line_count = count($lines);
  2193. for ($i = 0; $i < $line_count; $i++) {
  2194. if (!preg_match('/\|\s*$/', $lines[$i])) {
  2195. if ($i + 1 < $line_count) {
  2196. if ($i + 1 <= count($lines) - 1) { $lines[$i + 1] = $lines[$i] . "\n" . $lines[$i + 1]; }
  2197. } else {
  2198. $rows[] = $lines[$i];
  2199. }
  2200. } else {
  2201. $rows[] = $lines[$i];
  2202. }
  2203. }
  2204. unset($tid, $tpadl, $tpadr, $tlang);
  2205. $tclass = '';
  2206. $tstyle = '';
  2207. $talign = '';
  2208. if (preg_match('/^table[^\.]/', $rows[0])) {
  2209. $row = $rows[0];
  2210. $row = preg_replace('/^table/', '', $row, 1);
  2211. $params = 1;
  2212. // process row parameters until none are left
  2213. while ($params) {
  2214. if (preg_match('{^(' . $this->tblalignre . ')}', $row, $matches)) {
  2215. // found row alignment
  2216. $talign .= $matches[1];
  2217. if ($matches[1]) { $row = substr($row, strlen($matches[1])); }
  2218. if ($matches[1]) { continue; }
  2219. }
  2220. if (preg_match('{^(' . $this->clstypadre . ')}x', $row, $matches)) {
  2221. // found a class/id/style/padding indicator
  2222. $clsty = $matches[1];
  2223. if ($clsty) { $row = substr($row, strlen($clsty)); }
  2224. if (preg_match('/{([^}]+)}/', $clsty, $matches)) {
  2225. $tstyle = $matches[1];
  2226. $clsty = preg_replace('/{([^}]+)}/', '', $clsty, 1);
  2227. if ($tstyle) { continue; }
  2228. }
  2229. if (preg_match('/\(([A-Za-z0-9_\- ]+?)(?:#(.+?))?\)/', $clsty, $matches) ||
  2230. preg_match('/\(([A-Za-z0-9_\- ]+?)?(?:#(.+?))\)/', $clsty, $matches)) {
  2231. if ($matches[1] || $matches[2]) {
  2232. $tclass = $matches[1];
  2233. $tid = $matches[2];
  2234. continue;
  2235. }
  2236. }
  2237. if (preg_match('/(\(+)/', $clsty, $matches)) { $tpadl = strlen($matches[1]); }
  2238. if (preg_match('/(\)+)/', $clsty, $matches)) { $tpadr = strlen($matches[1]); }
  2239. if (preg_match('/\[(.+?)\]/', $clsty, $matches)) { $tlang = $matches[1]; }
  2240. if ($clsty) { continue; }
  2241. }
  2242. $params = 0;
  2243. }
  2244. $row = preg_replace('/\.\s+/', '', $row, 1);
  2245. $rows[0] = $row;
  2246. }
  2247. $out = '';
  2248. $cols = preg_split('/\|/', $rows[0] . ' ');
  2249. unset($colaligns, $rowspans);
  2250. foreach ($rows as $row) {
  2251. $cols = preg_split('/\|/', $row . ' ');
  2252. $colcount = count($cols) - 1;
  2253. array_pop($cols);
  2254. $colspan = 0;
  2255. $row_out = '';
  2256. unset($rowclass, $rowid, $rowalign, $rowstyle, $rowheader);
  2257. if (!$cols[0]) { $cols[0] = ''; }
  2258. if (preg_match('/_/', $cols[0])) {
  2259. $cols[0] = preg_replace('/_/', '', $cols[0]);
  2260. $rowheader = 1;
  2261. }
  2262. if (preg_match('/{([^}]+)}/', $cols[0], $matches)) {
  2263. $rowstyle = $matches[1];
  2264. $cols[0] = preg_replace('/{[^}]+}/', '', $cols[0]);
  2265. }
  2266. if (preg_match('/\(([^\#]+?)?(#(.+))?\)/', $cols[0], $matches)) {
  2267. $rowclass = $matches[1];
  2268. $rowid = $matches[3];
  2269. $cols[0] = preg_replace('/\([^\)]+\)/', '', $cols[0]);
  2270. }
  2271. if (preg_match('{(' . $this->alignre . ')}', $cols[0], $matches)) { $rowalign = $matches[1]; }
  2272. for ($c = $colcount - 1; $c > 0; $c--) {
  2273. if ($rowspans[$c]) {
  2274. $rowspans[$c]--;
  2275. if ($rowspans[$c] > 1) { continue; }
  2276. }
  2277. unset($colclass, $colid, $header, $colparams, $colpadl, $colpadr, $collang);
  2278. $colstyle = '';
  2279. $colalign = $colaligns[$c];
  2280. $col = array_pop($cols);
  2281. $col = ($col ? $col : '');
  2282. $attrs = '';
  2283. if (preg_match('{^(((_|[/\\\\]\d+|' . $this->alignre . '|' . $this->clstypadre . ')+)\.\ )}x', $col, $matches)) {
  2284. $colparams = $matches[2];
  2285. $col = substr($col, strlen($matches[1]));
  2286. $params = 1;
  2287. // keep processing column parameters until there
  2288. // are none left...
  2289. while ($params) {
  2290. if (preg_match('{^(_|' . $this->alignre . ')(.*)$}', $colparams, $matches)) {
  2291. // found alignment or heading indicator
  2292. $attrs .= $matches[1];
  2293. if ($matches[1]) { $colparams = $matches[2]; }
  2294. if ($matches[1]) { continue; }
  2295. }
  2296. if (preg_match('{^(' . $this->clstypadre . ')(.*)$}x', $colparams, $matches)) {
  2297. // found a class/id/style/padding marker
  2298. $clsty = $matches[1];
  2299. if ($clsty) { $colparams = $matches[2]; }
  2300. if (preg_match('/{([^}]+)}/', $clsty, $matches)) {
  2301. $colstyle = $matches[1];
  2302. $clsty = preg_replace('/{([^}]+)}/', '', $clsty, 1);
  2303. }
  2304. if (preg_match('/\(([A-Za-z0-9_\- ]+?)(?:#(.+?))?\)/', $clsty, $matches) ||
  2305. preg_match('/\(([A-Za-z0-9_\- ]+?)?(?:#(.+?))\)/', $clsty, $matches)) {
  2306. if ($matches[1] || $matches[2]) {
  2307. $colclass = $matches[1];
  2308. $colid = $matches[2];
  2309. if ($colclass) {
  2310. $clsty = preg_replace('/\([A-Za-z0-9_\- ]+?(#.*?)?\)/', '', $clsty);
  2311. } elseif ($colid) {
  2312. $clsty = preg_replace('/\(#.+?\)/', '', $clsty);
  2313. }
  2314. }
  2315. }
  2316. if (preg_match('/(\(+)/', $clsty, $matches)) {
  2317. $colpadl = strlen($matches[1]);
  2318. $clsty = preg_replace('/\(+/', '', $clsty, 1);
  2319. }
  2320. if (preg_match('/(\)+)/', $clsty, $matches)) {
  2321. $colpadr = strlen($matches[1]);
  2322. $clsty = preg_replace('/\)+/', '', $clsty, 1);
  2323. }
  2324. if (preg_match('/\[(.+?)\]/', $clsty, $matches)) {
  2325. $collang = $matches[1];
  2326. $clsty = preg_replace('/\[.+?\]/', '', $clsty, 1);
  2327. }
  2328. if ($clsty) { continue; }
  2329. }
  2330. if (preg_match('/^\\\\(\d+)/', $colparams, $matches)) {
  2331. $colspan = $matches[1];
  2332. $colparams = substr($colparams, strlen($matches[1]) + 1);
  2333. if ($matches[1]) { continue; }
  2334. }
  2335. if (preg_match('/\/(\d+)/', $colparams, $matches)) {
  2336. if ($matches[1]) { $rowspans[$c] = $matches[1]; }
  2337. $colparams = substr($colparams, strlen($matches[1]) + 1);
  2338. if ($matches[1]) { continue; }
  2339. }
  2340. $params = 0;
  2341. }
  2342. }
  2343. if (strlen($attrs)) {
  2344. if (preg_match('/_/', $attrs)) { $header = 1; }
  2345. if (preg_match('{(' . $this->alignre . ')}', $attrs, $matches) && strlen($matches[1])) { $colalign = ''; }
  2346. // determine column alignment
  2347. if (preg_match('/<>/', $attrs)) {
  2348. $colalign .= '<>';
  2349. } elseif (preg_match('/</', $attrs)) {
  2350. $colalign .= '<';
  2351. } elseif (preg_match('/=/', $attrs)) {
  2352. $colalign = '=';
  2353. } elseif (preg_match('/>/', $attrs)) {
  2354. $colalign = '>';
  2355. }
  2356. if (preg_match('/\^/', $attrs)) {
  2357. $colalign .= '^';
  2358. } elseif (preg_match('/~/', $attrs)) {
  2359. $colalign .= '~';
  2360. } elseif (preg_match('/-/', $attrs)) {
  2361. $colalign .= '-';
  2362. }
  2363. }
  2364. if ($rowheader) { $header = 1; }
  2365. if ($header) { $colaligns[$c] = $colalign; }
  2366. $col = preg_replace('/^ +/', '', $col, 1); $col = preg_replace('/ +$/', '', $col, 1);
  2367. if (strlen($col)) {
  2368. // create one cell tag
  2369. $rowspan = ($rowspans[$c] ? $rowspans[$c] : 0);
  2370. $col_out = '<' . ($header ? 'th' : 'td');
  2371. if ($colalign) {
  2372. // horizontal, vertical alignment
  2373. $halign = $this->_halign($colalign);
  2374. if ($halign) { $col_out .= " align=\"$halign\""; }
  2375. $valign = $this->_valign($colalign);
  2376. if ($valign) { $col_out .= " valign=\"$valign\""; }
  2377. }
  2378. // apply css attributes, row, column spans
  2379. if ($colpadl) { $colstyle .= ";padding-left:${colpadl}em"; }
  2380. if ($colpadr) { $colstyle .= ";padding-right:${colpadr}em"; }
  2381. if ($colclass) { $col_out .= " class=\"$colclass\""; }
  2382. if ($colid) { $col_out .= " id=\"$colid\""; }
  2383. if ($colstyle) { $colstyle = preg_replace('/^;/', '', $colstyle, 1); }
  2384. if ($colstyle) { $col_out .= " style=\"$colstyle\""; }
  2385. if ($collang) { $col_out .= " lang=\"$collang\""; }
  2386. if ($colspan > 1) { $col_out .= " colspan=\"$colspan\""; }
  2387. if ($rowspan > 1) { $col_out .= " rowspan=\"$rowspan\""; }
  2388. $col_out .= '>';
  2389. // if the content of this cell has newlines OR matches
  2390. // our paragraph block signature, process it as a full-blown
  2391. // textile document
  2392. if (preg_match('/\n\n/', $col) ||
  2393. preg_match('{^(?:' . $this->halignre . '|' . $this->clstypadre . '*)*
  2394. [\*\#]
  2395. (?:' . $this->clstypadre . '*|' . $this->halignre . ')*\ }x', $col)) {
  2396. $col_out .= $this->process($col);
  2397. } else {
  2398. $col_out .= $this->format_paragraph(array('text' => $col));
  2399. }
  2400. $col_out .= '</' . ($header ? 'th' : 'td') . '>';
  2401. $row_out = $col_out . $row_out;
  2402. if ($colspan) { $colspan = 0; }
  2403. } else {
  2404. if ($colspan == 0) { $colspan = 1; }
  2405. $colspan++;
  2406. }
  2407. }
  2408. if ($colspan > 1) {
  2409. // handle the spanned column if we came up short
  2410. $colspan--;
  2411. $row_out = "<td"
  2412. . ($colspan > 1 ? " colspan=\"$colspan\"" : '')
  2413. . "></td>$row_out";
  2414. }
  2415. // build one table row
  2416. $out .= "<tr";
  2417. if ($rowalign) {
  2418. $valign = $this->_valign($rowalign);
  2419. if ($valign) { $out .= " valign=\"$valign\""; }
  2420. }
  2421. if ($rowclass) { $out .= " class=\"$rowclass\""; }
  2422. if ($rowid) { $out .= " id=\"$rowid\""; }
  2423. if ($rowstyle) { $out .= " style=\"$rowstyle\""; }
  2424. $out .= ">$row_out</tr>";
  2425. }
  2426. // now, form the table tag itself
  2427. $table = '';
  2428. $table .= "<table";
  2429. if ($talign) {
  2430. if ($this->options['css_mode']) {
  2431. // horizontal alignment
  2432. $alignment = $this->_halign($talign);
  2433. if ($talign == '=') {
  2434. $tstyle .= ';margin-left:auto;margin-right:auto';
  2435. } else {
  2436. if ($alignment) { $tstyle .= ';float:' . $alignment; }
  2437. }
  2438. if ($alignment) { $tclass .= ' ' . $alignment; }
  2439. } else {
  2440. $alignment = $this->_halign($talign);
  2441. if ($alignment) { $table .= " align=\"$alignment\""; }
  2442. }
  2443. }
  2444. if ($tpadl) { $tstyle .= ";padding-left:${tpadl}em"; }
  2445. if ($tpadr) { $tstyle .= ";padding-right:${tpadr}em"; }
  2446. if ($tclass) { $tclass = preg_replace('/^ /', '', $tclass, 1); }
  2447. if ($tclass) { $table .= " class=\"$tclass\""; }
  2448. if ($tid) { $table .= " id=\"$tid\""; }
  2449. if ($tstyle) { $tstyle = preg_replace('/^;/', '', $tstyle, 1); }
  2450. if ($tstyle) { $table .= " style=\"$tstyle\""; }
  2451. if ($tlang) { $table .= " lang=\"$tlang\""; }
  2452. if ($tclass || $tid || $tstyle) { $table .= " cellspacing=\"0\""; }
  2453. $table .= ">$out</table>";
  2454. if (preg_match('|<tr></tr>|', $table)) {
  2455. // exception -- something isn't right so return fail case
  2456. return NULL;
  2457. }
  2458. return $table;
  2459. } // function format_table
  2460. /**
  2461. * The following attributes are allowed:
  2462. *
  2463. * <ul>
  2464. *
  2465. * <li><b>text</b>
  2466. *
  2467. * The text to be processed.</li>
  2468. *
  2469. * <li><b>filters</b>
  2470. *
  2471. * An array reference of filter names to run for the given text.</li>
  2472. *
  2473. * </ul>
  2474. *
  2475. * @param $args An @c array specifying the text and filters to
  2476. * apply.
  2477. *
  2478. * @return A @c string containing the filtered text.
  2479. *
  2480. * @private
  2481. */
  2482. function apply_filters($args) {
  2483. $text = $args['text'];
  2484. if (!$text) { return ''; }
  2485. $list = $args['filters'];
  2486. $filters = $this->options['filters'];
  2487. if (!is_array($filters)) { return $text; }
  2488. $param = $this->filter_param();
  2489. foreach ($list as $filter) {
  2490. if (!isset($filters[$filter])) { continue; }
  2491. if (is_string($filters[$filter])) {
  2492. $text = (($f = create_function('$text, $param', $filters[$filter])) ? $f($text, $param) : $text);
  2493. }
  2494. }
  2495. return $text;
  2496. } // function apply_filters
  2497. // minor utility / formatting routines
  2498. var $Have_Entities = 1;
  2499. /**
  2500. * Encodes input @c $html string, escaping characters as needed
  2501. * to HTML entities. This relies on the @c htmlentities function
  2502. * for full effect. If unavailable, @c encode_html_basic is used
  2503. * as a fallback technique. If the "char_encoding" flag is
  2504. * set to false, @c encode_html_basic is used exclusively.
  2505. *
  2506. * @param $html A @c string specifying the HTML to be encoded.
  2507. * @param $can_double_encode If provided, a @c bool indicating
  2508. * whether or not ampersand characters should be
  2509. * unconditionally encoded.
  2510. *
  2511. * @return A @c string containing the encoded HTML.
  2512. *
  2513. * @private
  2514. */
  2515. function encode_html($html, $can_double_encode = FALSE) {
  2516. if (!$html) { return ''; }
  2517. if ($this->Have_Entities && $this->options['char_encoding']) {
  2518. $html = htmlentities($html);
  2519. } else {
  2520. $html = $this->encode_html_basic($html, $can_double_encode);
  2521. }
  2522. return $html;
  2523. } // function encode_html
  2524. /**
  2525. * Decodes HTML entities in @c $html to their natural character
  2526. * equivalents.
  2527. *
  2528. * @param $html A @c string specifying the HTML to be decoded.
  2529. *
  2530. * @return A @c string containing the decode HTML
  2531. *
  2532. * @private
  2533. */
  2534. function decode_html($html) {
  2535. $html = preg_replace('!&quot;!', '"', $html);
  2536. $html = preg_replace('!&amp;!', '&', $html);
  2537. $html = preg_replace('!&lt;!', '<', $html);
  2538. $html = preg_replace('!&gt;!', '>', $html);
  2539. return $html;
  2540. } // function decode_html
  2541. /**
  2542. * Encodes the input @c $html string for the following characters:
  2543. * \<, \>, & and ". If @c $can_double_encode is true, all
  2544. * ampersand characters are escaped even if they already were.
  2545. * If @c $can_double_encode is false, ampersands are only escaped
  2546. * when they aren't part of a HTML entity already.
  2547. *
  2548. * @param $html A @c string specifying the HTML to be encoded.
  2549. * @param $can_double_encode If provided, a @c bool indicating
  2550. * whether or not ampersand characters should be
  2551. * unconditionally encoded.
  2552. *
  2553. * @return A @c string containing the encoded HTML.
  2554. *
  2555. * @private
  2556. */
  2557. function encode_html_basic($html, $can_double_encode = FALSE) {
  2558. if (!$html) { return ''; }
  2559. if (!preg_match('/[^\w\s]/', $html)) { return $html; }
  2560. if ($can_double_encode) {
  2561. $html = preg_replace('!&!', '&amp;', $html);
  2562. } else {
  2563. // Encode any & not followed by something that looks like
  2564. // an entity, numeric or otherwise.
  2565. $html = preg_replace('/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w{1,8});)/', '&amp;', $html);
  2566. }
  2567. $html = preg_replace('!"!', '&quot;', $html);
  2568. $html = preg_replace('!<!', '&lt;', $html);
  2569. $html = preg_replace('!>!', '&gt;', $html);
  2570. return $html;
  2571. } // function encode_html_basic
  2572. /**
  2573. * Returns the size for the image identified in @c $file. This
  2574. * method relies upon the @c getimagesize function. If unavailable,
  2575. * @c image_size will return @c NULL. Otherwise, the expected return
  2576. * value is an array of the width and height (in that order), in
  2577. * pixels.
  2578. *
  2579. * @param $file A @c string specifying the path or URL for the image
  2580. * file.
  2581. *
  2582. * @return An @c array containing the width and height
  2583. * (respectively) of the image.
  2584. *
  2585. * @private
  2586. */
  2587. function image_size($file) {
  2588. $Have_ImageSize = function_exists('getimagesize');
  2589. if ($Have_ImageSize) {
  2590. if (file_exists($file)) {
  2591. return @getimagesize($file);
  2592. } else {
  2593. if ($docroot = ($this->docroot() ? $this->docroot() : $_SERVER['DOCUMENT_ROOT'])) {
  2594. $fullpath = $docroot . preg_replace('|^/*(.*)$|', '/$1', $file);
  2595. if (file_exists($fullpath)) {
  2596. return @getimagesize($fullpath);
  2597. }
  2598. }
  2599. }
  2600. }
  2601. return @getimagesize($file);
  2602. } // function image_size
  2603. /**
  2604. * Encodes the query portion of a URL, escaping characters
  2605. * as necessary.
  2606. *
  2607. * @param $str A @c string specifying the URL to be encoded.
  2608. *
  2609. * @return A @c string containing the encoded URL.
  2610. *
  2611. * @private
  2612. */
  2613. function encode_url($str) {
  2614. $str = preg_replace_callback('!([^A-Za-z0-9_\.\-\+\&=%;])!x',
  2615. $this->_cb('ord($m[1]) > 255 ? \'%u\' . sprintf("%04X", ord($m[1]))
  2616. : \'%\' . sprintf("%02X", ord($m[1]))'), $str);
  2617. return $str;
  2618. } // function encode_url
  2619. /**
  2620. * Encodes the email address in @c $addr for 'mailto:' links.
  2621. *
  2622. * @param $addr A @c string specifying the email address to encode.
  2623. *
  2624. * @return A @c string containing the encoded email address.
  2625. *
  2626. * @private
  2627. */
  2628. function mail_encode($addr) {
  2629. // granted, this is simple, but it gives off warm fuzzies
  2630. $addr = preg_replace_callback('!([^\$])!x',
  2631. $this->_cb('ord($m[1]) > 255 ? \'%u\' . sprintf("%04X", ord($m[1]))
  2632. : \'%\' . sprintf("%02X", ord($m[1]))'), $addr);
  2633. return $addr;
  2634. } // function mail_encode
  2635. /**
  2636. * Processes string, formatting plain quotes into curly quotes.
  2637. *
  2638. * @param $str A @c string specifying the text to process.
  2639. *
  2640. * @return A @c string containing the processed text.
  2641. *
  2642. * @private
  2643. */
  2644. function process_quotes($str) {
  2645. // stub routine for now. subclass and implement.
  2646. return $str;
  2647. } // function process_quotes
  2648. // a default set of macros for the {...} macro syntax
  2649. // just a handy way to write a lot of the international characters
  2650. // and some commonly used symbols
  2651. /**
  2652. * Returns an associative @c array of macros that are assigned to be processed by
  2653. * default within the @c format_inline method.
  2654. *
  2655. * @return An @c array containing the default macros.
  2656. *
  2657. * @private
  2658. */
  2659. function default_macros() {
  2660. // <, >, " must be html entities in the macro text since
  2661. // those values are escaped by the time they are processed
  2662. // for macros.
  2663. return array(
  2664. 'c|' => '&#162;', // CENT SIGN
  2665. '|c' => '&#162;', // CENT SIGN
  2666. 'L-' => '&#163;', // POUND SIGN
  2667. '-L' => '&#163;', // POUND SIGN
  2668. 'Y=' => '&#165;', // YEN SIGN
  2669. '=Y' => '&#165;', // YEN SIGN
  2670. '(c)' => '&#169;', // COPYRIGHT SIGN
  2671. '&lt;&lt;' => '&#171;', // LEFT-POINTING DOUBLE ANGLE QUOTATION
  2672. '(r)' => '&#174;', // REGISTERED SIGN
  2673. '+_' => '&#177;', // PLUS-MINUS SIGN
  2674. '_+' => '&#177;', // PLUS-MINUS SIGN
  2675. '&gt;&gt;' => '&#187;', // RIGHT-POINTING DOUBLE ANGLE QUOTATION
  2676. '1/4' => '&#188;', // VULGAR FRACTION ONE QUARTER
  2677. '1/2' => '&#189;', // VULGAR FRACTION ONE HALF
  2678. '3/4' => '&#190;', // VULGAR FRACTION THREE QUARTERS
  2679. 'A`' => '&#192;', // LATIN CAPITAL LETTER A WITH GRAVE
  2680. '`A' => '&#192;', // LATIN CAPITAL LETTER A WITH GRAVE
  2681. 'A\'' => '&#193;', // LATIN CAPITAL LETTER A WITH ACUTE
  2682. '\'A' => '&#193;', // LATIN CAPITAL LETTER A WITH ACUTE
  2683. 'A^' => '&#194;', // LATIN CAPITAL LETTER A WITH CIRCUMFLEX
  2684. '^A' => '&#194;', // LATIN CAPITAL LETTER A WITH CIRCUMFLEX
  2685. 'A~' => '&#195;', // LATIN CAPITAL LETTER A WITH TILDE
  2686. '~A' => '&#195;', // LATIN CAPITAL LETTER A WITH TILDE
  2687. 'A"' => '&#196;', // LATIN CAPITAL LETTER A WITH DIAERESIS
  2688. '"A' => '&#196;', // LATIN CAPITAL LETTER A WITH DIAERESIS
  2689. 'Ao' => '&#197;', // LATIN CAPITAL LETTER A WITH RING ABOVE
  2690. 'oA' => '&#197;', // LATIN CAPITAL LETTER A WITH RING ABOVE
  2691. 'AE' => '&#198;', // LATIN CAPITAL LETTER AE
  2692. 'C,' => '&#199;', // LATIN CAPITAL LETTER C WITH CEDILLA
  2693. ',C' => '&#199;', // LATIN CAPITAL LETTER C WITH CEDILLA
  2694. 'E`' => '&#200;', // LATIN CAPITAL LETTER E WITH GRAVE
  2695. '`E' => '&#200;', // LATIN CAPITAL LETTER E WITH GRAVE
  2696. 'E\'' => '&#201;', // LATIN CAPITAL LETTER E WITH ACUTE
  2697. '\'E' => '&#201;', // LATIN CAPITAL LETTER E WITH ACUTE
  2698. 'E^' => '&#202;', // LATIN CAPITAL LETTER E WITH CIRCUMFLEX
  2699. '^E' => '&#202;', // LATIN CAPITAL LETTER E WITH CIRCUMFLEX
  2700. 'E"' => '&#203;', // LATIN CAPITAL LETTER E WITH DIAERESIS
  2701. '"E' => '&#203;', // LATIN CAPITAL LETTER E WITH DIAERESIS
  2702. 'I`' => '&#204;', // LATIN CAPITAL LETTER I WITH GRAVE
  2703. '`I' => '&#204;', // LATIN CAPITAL LETTER I WITH GRAVE
  2704. 'I\'' => '&#205;', // LATIN CAPITAL LETTER I WITH ACUTE
  2705. '\'I' => '&#205;', // LATIN CAPITAL LETTER I WITH ACUTE
  2706. 'I^' => '&#206;', // LATIN CAPITAL LETTER I WITH CIRCUMFLEX
  2707. '^I' => '&#206;', // LATIN CAPITAL LETTER I WITH CIRCUMFLEX
  2708. 'I"' => '&#207;', // LATIN CAPITAL LETTER I WITH DIAERESIS
  2709. '"I' => '&#207;', // LATIN CAPITAL LETTER I WITH DIAERESIS
  2710. 'D-' => '&#208;', // LATIN CAPITAL LETTER ETH
  2711. '-D' => '&#208;', // LATIN CAPITAL LETTER ETH
  2712. 'N~' => '&#209;', // LATIN CAPITAL LETTER N WITH TILDE
  2713. '~N' => '&#209;', // LATIN CAPITAL LETTER N WITH TILDE
  2714. 'O`' => '&#210;', // LATIN CAPITAL LETTER O WITH GRAVE
  2715. '`O' => '&#210;', // LATIN CAPITAL LETTER O WITH GRAVE
  2716. 'O\'' => '&#211;', // LATIN CAPITAL LETTER O WITH ACUTE
  2717. '\'O' => '&#211;', // LATIN CAPITAL LETTER O WITH ACUTE
  2718. 'O^' => '&#212;', // LATIN CAPITAL LETTER O WITH CIRCUMFLEX
  2719. '^O' => '&#212;', // LATIN CAPITAL LETTER O WITH CIRCUMFLEX
  2720. 'O~' => '&#213;', // LATIN CAPITAL LETTER O WITH TILDE
  2721. '~O' => '&#213;', // LATIN CAPITAL LETTER O WITH TILDE
  2722. 'O"' => '&#214;', // LATIN CAPITAL LETTER O WITH DIAERESIS
  2723. '"O' => '&#214;', // LATIN CAPITAL LETTER O WITH DIAERESIS
  2724. 'O/' => '&#216;', // LATIN CAPITAL LETTER O WITH STROKE
  2725. '/O' => '&#216;', // LATIN CAPITAL LETTER O WITH STROKE
  2726. 'U`' => '&#217;', // LATIN CAPITAL LETTER U WITH GRAVE
  2727. '`U' => '&#217;', // LATIN CAPITAL LETTER U WITH GRAVE
  2728. 'U\'' => '&#218;', // LATIN CAPITAL LETTER U WITH ACUTE
  2729. '\'U' => '&#218;', // LATIN CAPITAL LETTER U WITH ACUTE
  2730. 'U^' => '&#219;', // LATIN CAPITAL LETTER U WITH CIRCUMFLEX
  2731. '^U' => '&#219;', // LATIN CAPITAL LETTER U WITH CIRCUMFLEX
  2732. 'U"' => '&#220;', // LATIN CAPITAL LETTER U WITH DIAERESIS
  2733. '"U' => '&#220;', // LATIN CAPITAL LETTER U WITH DIAERESIS
  2734. 'Y\'' => '&#221;', // LATIN CAPITAL LETTER Y WITH ACUTE
  2735. '\'Y' => '&#221;', // LATIN CAPITAL LETTER Y WITH ACUTE
  2736. 'a`' => '&#224;', // LATIN SMALL LETTER A WITH GRAVE
  2737. '`a' => '&#224;', // LATIN SMALL LETTER A WITH GRAVE
  2738. 'a\'' => '&#225;', // LATIN SMALL LETTER A WITH ACUTE
  2739. '\'a' => '&#225;', // LATIN SMALL LETTER A WITH ACUTE
  2740. 'a^' => '&#226;', // LATIN SMALL LETTER A WITH CIRCUMFLEX
  2741. '^a' => '&#226;', // LATIN SMALL LETTER A WITH CIRCUMFLEX
  2742. 'a~' => '&#227;', // LATIN SMALL LETTER A WITH TILDE
  2743. '~a' => '&#227;', // LATIN SMALL LETTER A WITH TILDE
  2744. 'a"' => '&#228;', // LATIN SMALL LETTER A WITH DIAERESIS
  2745. '"a' => '&#228;', // LATIN SMALL LETTER A WITH DIAERESIS
  2746. 'ao' => '&#229;', // LATIN SMALL LETTER A WITH RING ABOVE
  2747. 'oa' => '&#229;', // LATIN SMALL LETTER A WITH RING ABOVE
  2748. 'ae' => '&#230;', // LATIN SMALL LETTER AE
  2749. 'c,' => '&#231;', // LATIN SMALL LETTER C WITH CEDILLA
  2750. ',c' => '&#231;', // LATIN SMALL LETTER C WITH CEDILLA
  2751. 'e`' => '&#232;', // LATIN SMALL LETTER E WITH GRAVE
  2752. '`e' => '&#232;', // LATIN SMALL LETTER E WITH GRAVE
  2753. 'e\'' => '&#233;', // LATIN SMALL LETTER E WITH ACUTE
  2754. '\'e' => '&#233;', // LATIN SMALL LETTER E WITH ACUTE
  2755. 'e^' => '&#234;', // LATIN SMALL LETTER E WITH CIRCUMFLEX
  2756. '^e' => '&#234;', // LATIN SMALL LETTER E WITH CIRCUMFLEX
  2757. 'e"' => '&#235;', // LATIN SMALL LETTER E WITH DIAERESIS
  2758. '"e' => '&#235;', // LATIN SMALL LETTER E WITH DIAERESIS
  2759. 'i`' => '&#236;', // LATIN SMALL LETTER I WITH GRAVE
  2760. '`i' => '&#236;', // LATIN SMALL LETTER I WITH GRAVE
  2761. 'i\'' => '&#237;', // LATIN SMALL LETTER I WITH ACUTE
  2762. '\'i' => '&#237;', // LATIN SMALL LETTER I WITH ACUTE
  2763. 'i^' => '&#238;', // LATIN SMALL LETTER I WITH CIRCUMFLEX
  2764. '^i' => '&#238;', // LATIN SMALL LETTER I WITH CIRCUMFLEX
  2765. 'i"' => '&#239;', // LATIN SMALL LETTER I WITH DIAERESIS
  2766. '"i' => '&#239;', // LATIN SMALL LETTER I WITH DIAERESIS
  2767. 'n~' => '&#241;', // LATIN SMALL LETTER N WITH TILDE
  2768. '~n' => '&#241;', // LATIN SMALL LETTER N WITH TILDE
  2769. 'o`' => '&#242;', // LATIN SMALL LETTER O WITH GRAVE
  2770. '`o' => '&#242;', // LATIN SMALL LETTER O WITH GRAVE
  2771. 'o\'' => '&#243;', // LATIN SMALL LETTER O WITH ACUTE
  2772. '\'o' => '&#243;', // LATIN SMALL LETTER O WITH ACUTE
  2773. 'o^' => '&#244;', // LATIN SMALL LETTER O WITH CIRCUMFLEX
  2774. '^o' => '&#244;', // LATIN SMALL LETTER O WITH CIRCUMFLEX
  2775. 'o~' => '&#245;', // LATIN SMALL LETTER O WITH TILDE
  2776. '~o' => '&#245;', // LATIN SMALL LETTER O WITH TILDE
  2777. 'o"' => '&#246;', // LATIN SMALL LETTER O WITH DIAERESIS
  2778. '"o' => '&#246;', // LATIN SMALL LETTER O WITH DIAERESIS
  2779. ':-' => '&#247;', // DIVISION SIGN
  2780. '-:' => '&#247;', // DIVISION SIGN
  2781. 'o/' => '&#248;', // LATIN SMALL LETTER O WITH STROKE
  2782. '/o' => '&#248;', // LATIN SMALL LETTER O WITH STROKE
  2783. 'u`' => '&#249;', // LATIN SMALL LETTER U WITH GRAVE
  2784. '`u' => '&#249;', // LATIN SMALL LETTER U WITH GRAVE
  2785. 'u\'' => '&#250;', // LATIN SMALL LETTER U WITH ACUTE
  2786. '\'u' => '&#250;', // LATIN SMALL LETTER U WITH ACUTE
  2787. 'u^' => '&#251;', // LATIN SMALL LETTER U WITH CIRCUMFLEX
  2788. '^u' => '&#251;', // LATIN SMALL LETTER U WITH CIRCUMFLEX
  2789. 'u"' => '&#252;', // LATIN SMALL LETTER U WITH DIAERESIS
  2790. '"u' => '&#252;', // LATIN SMALL LETTER U WITH DIAERESIS
  2791. 'y\'' => '&#253;', // LATIN SMALL LETTER Y WITH ACUTE
  2792. '\'y' => '&#253;', // LATIN SMALL LETTER Y WITH ACUTE
  2793. 'y"' => '&#255;', // LATIN SMALL LETTER Y WITH DIAERESIS
  2794. '"y' => '&#255;', // LATIN SMALL LETTER Y WITH DIAERESIS
  2795. 'OE' => '&#338;', // LATIN CAPITAL LIGATURE OE
  2796. 'oe' => '&#339;', // LATIN SMALL LIGATURE OE
  2797. '*' => '&#8226;', // BULLET
  2798. 'Fr' => '&#8355;', // FRENCH FRANC SIGN
  2799. 'L=' => '&#8356;', // LIRA SIGN
  2800. '=L' => '&#8356;', // LIRA SIGN
  2801. 'Rs' => '&#8360;', // RUPEE SIGN
  2802. 'C=' => '&#8364;', // EURO SIGN
  2803. '=C' => '&#8364;', // EURO SIGN
  2804. 'tm' => '&#8482;', // TRADE MARK SIGN
  2805. '&lt;-' => '&#8592;', // LEFTWARDS ARROW
  2806. '-&gt;' => '&#8594;', // RIGHTWARDS ARROW
  2807. '&lt;=' => '&#8656;', // LEFTWARDS DOUBLE ARROW
  2808. '=&gt;' => '&#8658;', // RIGHTWARDS DOUBLE ARROW
  2809. '=/' => '&#8800;', // NOT EQUAL TO
  2810. '/=' => '&#8800;', // NOT EQUAL TO
  2811. '&lt;_' => '&#8804;', // LESS-THAN OR EQUAL TO
  2812. '_&lt;' => '&#8804;', // LESS-THAN OR EQUAL TO
  2813. '&gt;_' => '&#8805;', // GREATER-THAN OR EQUAL TO
  2814. '_&gt;' => '&#8805;', // GREATER-THAN OR EQUAL TO
  2815. ':(' => '&#9785;', // WHITE FROWNING FACE
  2816. ':)' => '&#9786;', // WHITE SMILING FACE
  2817. 'spade' => '&#9824;', // BLACK SPADE SUIT
  2818. 'club' => '&#9827;', // BLACK CLUB SUIT
  2819. 'heart' => '&#9829;', // BLACK HEART SUIT
  2820. 'diamond' => '&#9830;', // BLACK DIAMOND SUIT
  2821. );
  2822. } // function default_macros
  2823. // "private", internal routines
  2824. /**
  2825. * Sets the default CSS names for CSS controlled markup. This
  2826. * is an internal function that should not be called directly.
  2827. *
  2828. * @private
  2829. */
  2830. function _css_defaults() {
  2831. $css_defaults = array(
  2832. 'class_align_right' => 'right',
  2833. 'class_align_left' => 'left',
  2834. 'class_align_center' => 'center',
  2835. 'class_align_top' => 'top',
  2836. 'class_align_bottom' => 'bottom',
  2837. 'class_align_middle' => 'middle',
  2838. 'class_align_justify' => 'justify',
  2839. 'class_caps' => 'caps',
  2840. 'class_footnote' => 'footnote',
  2841. 'id_footnote_prefix' => 'fn',
  2842. );
  2843. $this->css($css_defaults);
  2844. } // function _css_defaults
  2845. /**
  2846. * Returns the alignment keyword depending on the symbol passed.
  2847. *
  2848. * <ul>
  2849. *
  2850. * <li><b><code>\<\></code></b>
  2851. *
  2852. * becomes 'justify'</li>
  2853. *
  2854. * <li><b><code>\<</code></b>
  2855. *
  2856. * becomes 'left'</li>
  2857. *
  2858. * <li><b><code>\></code></b>
  2859. *
  2860. * becomes 'right'</li>
  2861. *
  2862. * <li><b><code>=</code></b>
  2863. *
  2864. * becomes 'center'</li>
  2865. *
  2866. * </ul>
  2867. *
  2868. * @param $align A @c string specifying the alignment code.
  2869. *
  2870. * @return A @c string containing the alignment text.
  2871. *
  2872. * @private
  2873. */
  2874. function _halign($align) {
  2875. if (preg_match('/<>/', $align)) {
  2876. return 'justify';
  2877. } elseif (preg_match('/</', $align)) {
  2878. return 'left';
  2879. } elseif (preg_match('/>/', $align)) {
  2880. return 'right';
  2881. } elseif (preg_match('/=/', $align)) {
  2882. return 'center';
  2883. }
  2884. return '';
  2885. } // function _halign
  2886. /**
  2887. * Returns the alignment keyword depending on the symbol passed.
  2888. *
  2889. * <ul>
  2890. *
  2891. * <li><b><code>^</code></b>
  2892. *
  2893. * becomes 'top'</li>
  2894. *
  2895. * <li><b><code>~</code></b>
  2896. *
  2897. * becomes 'bottom'</li>
  2898. *
  2899. * <li><b><code>-</code></b>
  2900. *
  2901. * becomes 'middle'</li>
  2902. *
  2903. * </ul>
  2904. *
  2905. * @param $align A @c string specifying the alignment code.
  2906. *
  2907. * @return A @c string containing the alignment text.
  2908. *
  2909. * @private
  2910. */
  2911. function _valign($align) {
  2912. if (preg_match('/\^/', $align)) {
  2913. return 'top';
  2914. } elseif (preg_match('/~/', $align)) {
  2915. return 'bottom';
  2916. } elseif (preg_match('/-/', $align)) {
  2917. return 'middle';
  2918. }
  2919. return '';
  2920. } // function _valign
  2921. /**
  2922. * Returns the alignment keyword depending on the symbol passed.
  2923. * The following alignment symbols are recognized, and given
  2924. * preference in the order listed:
  2925. *
  2926. * <ul>
  2927. *
  2928. * <li><b><code>^</code></b>
  2929. *
  2930. * becomes 'top'</li>
  2931. *
  2932. * <li><b><code>~</code></b>
  2933. *
  2934. * becomes 'bottom'</li>
  2935. *
  2936. * <li><b><code>-</code></b>
  2937. *
  2938. * becomes 'middle'</li>
  2939. *
  2940. * <li><b><code>\<</code></b>
  2941. *
  2942. * becomes 'left'</li>
  2943. *
  2944. * <li><b><code>\></code></b>
  2945. *
  2946. * becomes 'right'</li>
  2947. *
  2948. * </ul>
  2949. *
  2950. * @param $align A @c string containing the alignment code.
  2951. *
  2952. * @return A @c string containing the alignment text.
  2953. *
  2954. * @private
  2955. */
  2956. function _imgalign($align) {
  2957. $align = preg_replace('/(<>|=)/', '', $align);
  2958. return ($this->_valign($align) ? $this->_valign($align) : $this->_halign($align));
  2959. } // function _imgalign
  2960. /**
  2961. * This utility routine will take 'border' characters off of
  2962. * the given @c $pre and @c $post strings if they match one of these
  2963. * conditions:
  2964. * <pre>
  2965. * $pre starts with '[', $post ends with ']'
  2966. * $pre starts with '{', $post ends with '}'
  2967. * </pre>
  2968. * If neither condition is met, then the @c $pre and @c $post
  2969. * values are left untouched.
  2970. *
  2971. * @param $pre A @c string specifying the prefix.
  2972. * @param $post A @c string specifying the postfix.
  2973. *
  2974. * @private
  2975. */
  2976. function _strip_borders(&$pre, &$post) {
  2977. if ($post && $pre && preg_match('/[{[]/', ($open = substr($pre, 0, 1)))) {
  2978. $close = substr($post, 0, 1);
  2979. if ((($open == '{') && ($close == '}')) ||
  2980. (($open == '[') && ($close == ']'))) {
  2981. $pre = substr($pre, 1);
  2982. $post = substr($post, 1);
  2983. } else {
  2984. if (!preg_match('/[}\]]/', $close)) { $close = substr($post, -1, 1); }
  2985. if ((($open == '{') && ($close == '}')) ||
  2986. (($open == '[') && ($close == ']'))) {
  2987. $pre = substr($pre, 1);
  2988. $post = substr($post, 0, strlen($post) - 1);
  2989. }
  2990. }
  2991. }
  2992. } // function _strip_borders
  2993. /**
  2994. * An internal routine that takes a string and appends it to an array.
  2995. * It returns a marker that is used later to restore the preserved
  2996. * string.
  2997. *
  2998. * @param $array The @c array in which to store the replacement
  2999. * text.
  3000. * @param $str A @c string specifying the replacement text.
  3001. *
  3002. * @return A @c string containing a temporary marker for the
  3003. * replacement.
  3004. *
  3005. * @private
  3006. */
  3007. function _repl(&$array, $str) {
  3008. $array[] = $str;
  3009. return '<textile#' . count($array) . '>';
  3010. } // function _repl
  3011. /**
  3012. * An internal routine responsible for breaking up a string into
  3013. * individual tag and plaintext elements.
  3014. *
  3015. * @param $str A @c string specifying the text to tokenize.
  3016. *
  3017. * @return An @c array containing the tag and text tokens.
  3018. *
  3019. * @private
  3020. */
  3021. function _tokenize($str) {
  3022. $pos = 0;
  3023. $len = strlen($str);
  3024. unset($tokens);
  3025. $depth = 6;
  3026. $nested_tags = substr(str_repeat('(?:</?[A-Za-z0-9:]+ \s? (?:[^<>]|', $depth), 0, -1)
  3027. . str_repeat(')*>)', $depth);
  3028. $match = '(?s: <! ( -- .*? -- \s* )+ > )| # comment
  3029. (?s: <\? .*? \?> )| # processing instruction
  3030. (?s: <% .*? %> )| # ASP-like
  3031. (?:' . $nested_tags . ')|
  3032. (?:' . $this->codere . ')'; // nested tags
  3033. while (preg_match('{(' . $match . ')}x', substr($str, $pos), $matches, PREG_OFFSET_CAPTURE)) {
  3034. $whole_tag = $matches[1][0];
  3035. $sec_start = $pos + $matches[1][1] + strlen($whole_tag);
  3036. $tag_start = $sec_start - strlen($whole_tag);
  3037. if ($pos < $tag_start) {
  3038. $tokens[] = array('text', substr($str, $pos, $tag_start - $pos));
  3039. }
  3040. if (preg_match('/^[[{]?@/', $whole_tag)) {
  3041. $tokens[] = array('text', $whole_tag);
  3042. } else {
  3043. // this clever hack allows us to preserve \n within tags.
  3044. // this is restored at the end of the format_paragraph method
  3045. //$whole_tag = preg_replace('/\n/', "\r", $whole_tag);
  3046. $whole_tag = preg_replace('/\n/', "\001", $whole_tag);
  3047. $tokens[] = array('tag', $whole_tag);
  3048. }
  3049. $pos = $sec_start;
  3050. }
  3051. if ($pos < $len) { $tokens[] = array('text', substr($str, $pos, $len - $pos)); }
  3052. return $tokens;
  3053. } // function _tokenize
  3054. /**
  3055. * Returns the version of this release of Textile.php. *JHR*
  3056. *
  3057. * @return An @c array with keys 'text' and 'build' containing the
  3058. * text version and build ID of this release, respectively.
  3059. *
  3060. * @static
  3061. */
  3062. function version() {
  3063. /* Why text and an ID? Well, the text is easier for the user to
  3064. * read and understand while the build ID, being a number (a date
  3065. * with a serial, specifically), is easier for the developer to
  3066. * use to determine newer/older versions for upgrade and
  3067. * installation purposes.
  3068. */
  3069. return array("text" => "2.0.8", "build" => 2005032100);
  3070. } // function version
  3071. /**
  3072. * Creates a custom callback function from the provided PHP
  3073. * code. The result is used as the callback in
  3074. * @c preg_replace_callback calls. *JHR*
  3075. *
  3076. * @param $function A @c string specifying the PHP code for the
  3077. * function body.
  3078. *
  3079. * @return A @c function to be used for the callback.
  3080. *
  3081. * @private
  3082. */
  3083. function _cb($function) {
  3084. $current =& Textile::_current_store($this);
  3085. return create_function('$m', '$me =& Textile::_current(); return ' . $function . ';');
  3086. } // function _cb
  3087. /**
  3088. * Stores a static variable for the Textile class. This helper
  3089. * function is used by @c _current to simulate a static
  3090. * class variable in PHP. *JHR*
  3091. *
  3092. * @param $new If a non-@c NULL object reference, the Textile object
  3093. * to be set as the current object.
  3094. *
  3095. * @return The @c array containing a reference to the current
  3096. * Textile object at index 0. An array is used because PHP
  3097. * does not allow static variables to be references.
  3098. *
  3099. * @static
  3100. * @private
  3101. */
  3102. /* static */ function &_current_store(&$new) {
  3103. static $current = array();
  3104. if ($new != NULL) {
  3105. $current = array(&$new);
  3106. }
  3107. return $current;
  3108. } // function _current_store
  3109. /**
  3110. * Returns the "current" Textile object. This is used within
  3111. * anonymous callback functions which cannot have the scope of a
  3112. * specific object. *JHR*
  3113. *
  3114. * @return An @c object reference to the current Textile object.
  3115. *
  3116. * @static
  3117. * @private
  3118. */
  3119. /* static */ function &_current() {
  3120. $current =& Textile::_current_store($null = NULL);
  3121. return $current[0];
  3122. } // function _current
  3123. } // class Textile
  3124. /**
  3125. * Brad Choate's mttextile Movable Type plugin adds some additional
  3126. * functionality to the Textile.pm Perl module. This includes optional
  3127. * "SmartyPants" processing of text to produce smart quotes, dashes,
  3128. * etc., code colorizing using Beautifier, and some special lookup
  3129. * links (imdb, google, dict, and amazon). The @c MTLikeTextile class
  3130. * is a subclass of @c Textile that provides an MT-like implementation
  3131. * of Textile to produce results similar to that of the mttextile
  3132. * plugin. Currently only the SmartyPants and special lookup links are
  3133. * implemented.
  3134. *
  3135. * Using the @c MTLikeTextile class is exactly the same as using @c
  3136. * Textile. Simply use <code>$textile = new MTLikeTextile;</code>
  3137. * instead of <code>$textile = new Textile;</code> to create a Textile
  3138. * object. This will enable the special lookup links. To enable
  3139. * SmartyPants processing, you must install the SmartyPants-PHP
  3140. * implementation available at
  3141. * <a
  3142. * href="http://monauraljerk.org/smartypants-php/">http://monauraljerk.org/smartypants-php/</a>
  3143. * and include the
  3144. * SmartyPants-PHP.inc file.
  3145. *
  3146. * <pre><code>
  3147. * include_once("Textile.php");
  3148. * include_once("SmartyPants-PHP.inc");
  3149. * $text = \<\<\<EOT
  3150. * h1. Heading
  3151. *
  3152. * A _simple_ demonstration of Textile markup.
  3153. *
  3154. * * One
  3155. * * Two
  3156. * * Three
  3157. *
  3158. * "More information":http://www.textism.com/tools/textile is available.
  3159. * EOT;
  3160. *
  3161. * $textile = new MTLikeTextile;
  3162. * $html = $textile->process($text);
  3163. * print $html;
  3164. * </code></pre>
  3165. *
  3166. * @brief A Textile implementation providing additional
  3167. * Movable-Type-like formatting to produce results similar to
  3168. * the mttextile plugin.
  3169. *
  3170. * @author Jim Riggs \<textile at jimandlisa dot com\>
  3171. */
  3172. class MTLikeTextile extends Textile {
  3173. /**
  3174. * Instantiates a new MTLikeTextile object. Optional options
  3175. * can be passed to initialize the object. Attributes for the
  3176. * options key are the same as the get/set method names
  3177. * documented here.
  3178. *
  3179. * @param $options The @c array specifying the options to use for
  3180. * this object.
  3181. *
  3182. * @public
  3183. */
  3184. function MTLikeTextile($options = array()) {
  3185. parent::Textile($options);
  3186. } // function MTLikeTextile
  3187. /**
  3188. * @private
  3189. */
  3190. function process_quotes($str) {
  3191. if (!$this->options['do_quotes'] || !function_exists('SmartyPants')) {
  3192. return $str;
  3193. }
  3194. return SmartyPants($str, $this->options['smarty_mode']);
  3195. } // function process_quotes
  3196. /**
  3197. * @private
  3198. */
  3199. function format_url($args) {
  3200. $url = ($args['url'] ? $args['url'] : '');
  3201. if (preg_match('/^(imdb|google|dict|amazon)(:(.+))?$/x', $url, $matches)) {
  3202. $term = $matches[3];
  3203. $term = ($term ? $term : strip_tags($args['linktext']));
  3204. switch ($matches[1]) {
  3205. case 'imdb':
  3206. $args['url'] = 'http://www.imdb.com/Find?for=' . $term;
  3207. break;
  3208. case 'google':
  3209. $args['url'] = 'http://www.google.com/search?q=' . $term;
  3210. break;
  3211. case 'dict':
  3212. $args['url'] = 'http://www.dictionary.com/search?q=' . $term;
  3213. break;
  3214. case 'amazon':
  3215. $args['url'] = 'http://www.amazon.com/exec/obidos/external-search?index=blended&keyword=' . $term;
  3216. break;
  3217. }
  3218. }
  3219. return parent::format_url($args);
  3220. } // function format_url
  3221. } // class MTLikeTextile
  3222. /**
  3223. * @mainpage
  3224. * Textile - A Humane Web Text Generator.
  3225. *
  3226. * @section synopsis SYNOPSIS
  3227. *
  3228. * <pre><code>
  3229. * include_once("Textile.php");
  3230. * $text = \<\<\<EOT
  3231. * h1. Heading
  3232. *
  3233. * A _simple_ demonstration of Textile markup.
  3234. *
  3235. * * One
  3236. * * Two
  3237. * * Three
  3238. *
  3239. * "More information":http://www.textism.com/tools/textile is available.
  3240. * EOT;
  3241. *
  3242. * $textile = new Textile;
  3243. * $html = $textile->process($text);
  3244. * print $html;
  3245. * </code></pre>
  3246. *
  3247. * @section abstract ABSTRACT
  3248. *
  3249. * Textile.php is a PHP-based implementation of Dean Allen's Textile
  3250. * syntax. Textile is shorthand for doing common formatting tasks.
  3251. *
  3252. * @section syntax SYNTAX
  3253. *
  3254. * Textile processes text in units of blocks and lines.
  3255. * A block might also be considered a paragraph, since blocks
  3256. * are separated from one another by a blank line. Blocks
  3257. * can begin with a signature that helps identify the rest
  3258. * of the block content. Block signatures include:
  3259. *
  3260. * <ul>
  3261. *
  3262. * <li><b>p</b>
  3263. *
  3264. * A paragraph block. This is the default signature if no
  3265. * signature is explicitly given. Paragraphs are formatted
  3266. * with all the inline rules (see inline formatting) and
  3267. * each line receives the appropriate markup rules for
  3268. * the flavor of HTML in use. For example, newlines for XHTML
  3269. * content receive a \<br /\> tag at the end of the line
  3270. * (with the exception of the last line in the paragraph).
  3271. * Paragraph blocks are enclosed in a \<p\> tag.</li>
  3272. *
  3273. * <li><b>pre</b>
  3274. *
  3275. * A pre-formatted block of text. Textile will not add any
  3276. * HTML tags for individual lines. Whitespace is also preserved.
  3277. *
  3278. * Note that within a "pre" block, \< and \> are
  3279. * translated into HTML entities automatically.</li>
  3280. *
  3281. * <li><b>bc</b>
  3282. *
  3283. * A "bc" signature is short for "block code", which implies
  3284. * a preformatted section like the 'pre' block, but it also
  3285. * gets a \<code\> tag (or for XHTML 2, a \<blockcode\>
  3286. * tag is used instead).
  3287. *
  3288. * Note that within a "bc" block, \< and \> are
  3289. * translated into HTML entities automatically.</li>
  3290. *
  3291. * <li><b>table</b>
  3292. *
  3293. * For composing HTML tables. See the "TABLES" section for more
  3294. * information.</li>
  3295. *
  3296. * <li><b>bq</b>
  3297. *
  3298. * A "bq" signature is short for "block quote". Paragraph text
  3299. * formatting is applied to these blocks and they are enclosed
  3300. * in a \<blockquote\> tag as well as \<p\> tags
  3301. * within.</li>
  3302. *
  3303. * <li><b>h1, h2, h3, h4, h5, h6</b>
  3304. *
  3305. * Headline signatures that produce \<h1\>, etc. tags.
  3306. * You can adjust the relative output of these using the
  3307. * head_offset attribute.</li>
  3308. *
  3309. * <li><b>clear</b>
  3310. *
  3311. * A 'clear' signature is simply used to indicate that the next
  3312. * block should emit a CSS style attribute that clears any
  3313. * floating elements. The default behavior is to clear "both",
  3314. * but you can use the left (\< or right \>) alignment
  3315. * characters to indicate which side to clear.</li>
  3316. *
  3317. * <li><b>dl</b>
  3318. *
  3319. * A "dl" signature is short for "definition list". See the
  3320. * "LISTS" section for more information.</li>
  3321. *
  3322. * <li><b>fn</b>
  3323. *
  3324. * A "fn" signature is short for "footnote". You add a number
  3325. * following the "fn" keyword to number the footnote. Footnotes
  3326. * are output as paragraph tags but are given a special CSS
  3327. * class name which can be used to style them as you see fit.</li>
  3328. *
  3329. * </ul>
  3330. *
  3331. * All signatures should end with a period and be followed
  3332. * with a space. Inbetween the signature and the period, you
  3333. * may use several parameters to further customize the block.
  3334. * These include:
  3335. *
  3336. * <ul>
  3337. *
  3338. * <li><b><code>{style rule}</code></b>
  3339. *
  3340. * A CSS style rule. Style rules can span multiple lines.</li>
  3341. *
  3342. * <li><b><code>[ll]</code></b>
  3343. *
  3344. * A language identifier (for a "lang" attribute).</li>
  3345. *
  3346. * <li><b><code>(class)</code> or <code>(#id)</code> or <code>(class#id)</code></b>
  3347. *
  3348. * For CSS class and id attributes.</li>
  3349. *
  3350. * <li><b><code>\></code>, <code>\<</code>, <code>=</code>, <code>\<\></code></b>
  3351. *
  3352. * Modifier characters for alignment. Right-justification, left-justification,
  3353. * centered, and full-justification.</li>
  3354. *
  3355. * <li><b><code>(</code> (one or more)</b>
  3356. *
  3357. * Adds padding on the left. 1em per "(" character is applied.
  3358. * When combined with the align-left or align-right modifier,
  3359. * it makes the block float.</li>
  3360. *
  3361. * <li><b><code>)</code> (one or more)</b>
  3362. *
  3363. * Adds padding on the right. 1em per ")" character is applied.
  3364. * When combined with the align-left or align-right modifier,
  3365. * it makes the block float.</li>
  3366. *
  3367. * <li><b><code>|filter|</code> or <code>|filter|filter|filter|</code></b>
  3368. *
  3369. * A filter may be invoked to further format the text for this
  3370. * signature. If one or more filters are identified, the text
  3371. * will be processed first using the filters and then by
  3372. * Textile's own block formatting rules.</li>
  3373. *
  3374. * </ul>
  3375. *
  3376. * @subsection extendedblocks Extended Blocks
  3377. *
  3378. * Normally, a block ends with the first blank line encountered.
  3379. * However, there are situations where you may want a block to continue
  3380. * for multiple paragraphs of text. To cause a given block signature
  3381. * to stay active, use two periods in your signature instead of one.
  3382. * This will tell Textile to keep processing using that signature
  3383. * until it hits the next signature is found.
  3384. *
  3385. * For example:
  3386. * <pre>
  3387. * bq.. This is paragraph one of a block quote.
  3388. *
  3389. * This is paragraph two of a block quote.
  3390. *
  3391. * p. Now we're back to a regular paragraph.
  3392. * </pre>
  3393. * You can apply this technique to any signature (although for
  3394. * some it doesn't make sense, like "h1" for example). This is
  3395. * especially useful for "bc" blocks where your code may
  3396. * have many blank lines scattered through it.
  3397. *
  3398. * @subsection escaping Escaping
  3399. *
  3400. * Sometimes you want Textile to just get out of the way and
  3401. * let you put some regular HTML markup in your document. You
  3402. * can disable Textile formatting for a given block using the '=='
  3403. * escape mechanism:
  3404. * <pre>
  3405. * p. Regular paragraph
  3406. *
  3407. * ==
  3408. * Escaped portion -- will not be formatted
  3409. * by Textile at all
  3410. * ==
  3411. *
  3412. * p. Back to normal.
  3413. * </pre>
  3414. * You can also use this technique within a Textile block,
  3415. * temporarily disabling the inline formatting functions:
  3416. * <pre>
  3417. * p. This is ==*a test*== of escaping.
  3418. * </pre>
  3419. * @subsection inlineformatting Inline Formatting
  3420. *
  3421. * Formatting within a block of text is covered by the "inline"
  3422. * formatting rules. These operators must be placed up against
  3423. * text/punctuation to be recognized. These include:
  3424. *
  3425. * <ul>
  3426. *
  3427. * <li><b><code>*strong*</code></b>
  3428. *
  3429. * Translates into \<strong\>strong\</strong\>.</li>
  3430. *
  3431. * <li><b>_emphasis_</b>
  3432. *
  3433. * Translates into \<em\>emphasis\</em\>.</li>
  3434. *
  3435. * <li><b><code>**bold**</code></b>
  3436. *
  3437. * Translates into \<b\>bold\</b\>.</li>
  3438. *
  3439. * <li><b><code>__italics__</code></b>
  3440. *
  3441. * Translates into \<i\>italics\</i\>.</li>
  3442. *
  3443. * <li><b><code>++bigger++</code></b>
  3444. *
  3445. * Translates into \<big\>bigger\</big\>.</li>
  3446. *
  3447. * <li><b><code>--smaller--</code></b>
  3448. *
  3449. * Translates into: \<small\>smaller\</small\>.</li>
  3450. *
  3451. * <li><b><code>-deleted text-</code></b>
  3452. *
  3453. * Translates into \<del\>deleted text\</del\>.</li>
  3454. *
  3455. * <li><b><code>+inserted text+</code></b>
  3456. *
  3457. * Translates into \<ins\>inserted text\</ins\>.</li>
  3458. *
  3459. * <li><b><code>^superscript^</code></b>
  3460. *
  3461. * Translates into \<sup\>superscript\</sup\>.</li>
  3462. *
  3463. * <li><b><code>~subscript~</code></b>
  3464. *
  3465. * Translates into \<sub\>subscript\</sub\>.</li>
  3466. *
  3467. * <li><b><code>\%span\%</code></b>
  3468. *
  3469. * Translates into \<span\>span\</span\>.</li>
  3470. *
  3471. * <li><b><code>\@code\@</code></b>
  3472. *
  3473. * Translates into \<code\>code\</code\>. Note
  3474. * that within a '\@...\@' section, \< and \> are
  3475. * translated into HTML entities automatically.</li>
  3476. *
  3477. * </ul>
  3478. *
  3479. * Inline formatting operators accept the following modifiers:
  3480. *
  3481. * <ul>
  3482. *
  3483. * <li><b><code>{style rule}</code></b>
  3484. *
  3485. * A CSS style rule.</li>
  3486. *
  3487. * <li><b><code>[ll]</code></b>
  3488. *
  3489. * A language identifier (for a "lang" attribute).</li>
  3490. *
  3491. * <li><b><code>(class)</code> or <code>(#id)</code> or <code>(class#id)</code></b>
  3492. *
  3493. * For CSS class and id attributes.</li>
  3494. *
  3495. * </ul>
  3496. *
  3497. * @subsubsection examples Examples
  3498. * <pre>
  3499. * Textile is *way* cool.
  3500. *
  3501. * Textile is *_way_* cool.
  3502. * </pre>
  3503. * Now this won't work, because the formatting
  3504. * characters need whitespace before and after
  3505. * to be properly recognized.
  3506. * <pre>
  3507. * Textile is way c*oo*l.
  3508. * </pre>
  3509. * However, you can supply braces or brackets to
  3510. * further clarify that you want to format, so
  3511. * this would work:
  3512. * <pre>
  3513. * Textile is way c[*oo*]l.
  3514. * </pre>
  3515. * @subsection footnotes Footnotes
  3516. *
  3517. * You can create footnotes like this:
  3518. * <pre>
  3519. * And then he went on a long trip[1].
  3520. * </pre>
  3521. * By specifying the brackets with a number inside, Textile will
  3522. * recognize that as a footnote marker. It will replace that with
  3523. * a construct like this:
  3524. * <pre>
  3525. * And then he went on a long
  3526. * trip<sup class="footnote"><a href="#fn1">1</a></sup>
  3527. * </pre>
  3528. * To supply the content of the footnote, place it at the end of your
  3529. * document using a "fn" block signature:
  3530. * <pre>
  3531. * fn1. And there was much rejoicing.
  3532. * </pre>
  3533. * Which creates a paragraph that looks like this:
  3534. * <pre>
  3535. * <p class="footnote" id="fn1"><sup>1</sup> And there was
  3536. * much rejoicing.</p>
  3537. * </pre>
  3538. * @subsection links Links
  3539. *
  3540. * Textile defines a shorthand for formatting hyperlinks.
  3541. * The format looks like this:
  3542. * <pre>
  3543. * "Text to display":http://example.com
  3544. * </pre>
  3545. * In addition to this, you can add 'title' text to your link:
  3546. * <pre>
  3547. * "Text to display (Title text)":http://example.com
  3548. * </pre>
  3549. * The URL portion of the link supports relative paths as well
  3550. * as other protocols like ftp, mailto, news, telnet, etc.
  3551. * <pre>
  3552. * "E-mail me please":mailto:someone\@example.com
  3553. * </pre>
  3554. * You can also use single quotes instead of double-quotes if
  3555. * you prefer. As with the inline formatting rules, a hyperlink
  3556. * must be surrounded by whitespace to be recognized (an
  3557. * exception to this is common punctuation which can reside
  3558. * at the end of the URL). If you have to place a URL next to
  3559. * some other text, use the bracket or brace trick to do that:
  3560. * <pre>
  3561. * You["gotta":http://example.com]seethis!
  3562. * </pre>
  3563. * Textile supports an alternate way to compose links. You can
  3564. * optionally create a lookup list of links and refer to them
  3565. * separately. To do this, place one or more links in a block
  3566. * of it's own (it can be anywhere within your document):
  3567. * <pre>
  3568. * [excom]http://example.com
  3569. * [exorg]http://example.org
  3570. * </pre>
  3571. * For a list like this, the text in the square brackets is
  3572. * used to uniquely identify the link given. To refer to that
  3573. * link, you would specify it like this:
  3574. * <pre>
  3575. * "Text to display":excom
  3576. * </pre>
  3577. * Once you've defined your link lookup table, you can use
  3578. * the identifiers any number of times.
  3579. *
  3580. * @subsection images Images
  3581. *
  3582. * Images are identified by the following pattern:
  3583. * <pre>
  3584. * !/path/to/image!
  3585. * </pre>
  3586. * Image attributes may also be specified:
  3587. * <pre>
  3588. * !/path/to/image 10x20!
  3589. * </pre>
  3590. * Which will render an image 10 pixels wide and 20 pixels high.
  3591. * Another way to indicate width and height:
  3592. * <pre>
  3593. * !/path/to/image 10w 20h!
  3594. * </pre>
  3595. * You may also redimension the image using a percentage.
  3596. * <pre>
  3597. * !/path/to/image 20%x40%!
  3598. * </pre>
  3599. * Which will render the image at 20% of it's regular width
  3600. * and 40% of it's regular height.
  3601. *
  3602. * Or specify one percentage to resize proprotionately:
  3603. * <pre>
  3604. * !/path/to/image 20%!
  3605. * </pre>
  3606. * Alt text can be given as well:
  3607. * <pre>
  3608. * !/path/to/image (Alt text)!
  3609. * </pre>
  3610. * The path of the image may refer to a locally hosted image or
  3611. * can be a full URL.
  3612. *
  3613. * You can also use the following modifiers after the opening '!'
  3614. * character:
  3615. *
  3616. * <ul>
  3617. *
  3618. * <li><b><code>\<</code></b>
  3619. *
  3620. * Align the image to the left (causes the image to float if
  3621. * CSS options are enabled).</li>
  3622. *
  3623. * <li><b><code>\></code></b>
  3624. *
  3625. * Align the image to the right (causes the image to float if
  3626. * CSS options are enabled).</li>
  3627. *
  3628. * <li><b><code>-</code> (dash)</b>
  3629. *
  3630. * Aligns the image to the middle.</li>
  3631. *
  3632. * <li><b><code>^</code></b>
  3633. *
  3634. * Aligns the image to the top.</li>
  3635. *
  3636. * <li><b><code>~</code> (tilde)</b>
  3637. *
  3638. * Aligns the image to the bottom.</li>
  3639. *
  3640. * <li><b><code>{style rule}</code></b>
  3641. *
  3642. * Applies a CSS style rule to the image.</li>
  3643. *
  3644. * <li><b><code>(class)</code> or <code>(#id)</code> or <code>(class#id)</code></b>
  3645. *
  3646. * Applies a CSS class and/or id to the image.</li>
  3647. *
  3648. * <li><b><code>(</code> (one or more)</b>
  3649. *
  3650. * Pads 1em on the left for each '(' character.</li>
  3651. *
  3652. * <li><b><code>)</code> (one or more)</b>
  3653. *
  3654. * Pads 1em on the right for each ')' character.</li>
  3655. *
  3656. * </ul>
  3657. *
  3658. * @subsection characterreplacements Character Replacements
  3659. *
  3660. * A few simple, common symbols are automatically replaced:
  3661. * <pre>
  3662. * (c)
  3663. * (r)
  3664. * (tm)
  3665. * </pre>
  3666. * In addition to these, there are a whole set of character
  3667. * macros that are defined by default. All macros are enclosed
  3668. * in curly braces. These include:
  3669. * <pre>
  3670. * {c|} or {|c} cent sign
  3671. * {L-} or {-L} pound sign
  3672. * {Y=} or {=Y} yen sign
  3673. * </pre>
  3674. * Many of these macros can be guessed. For example:
  3675. * <pre>
  3676. * {A'} or {'A}
  3677. * {a"} or {"a}
  3678. * {1/4}
  3679. * {*}
  3680. * {:)}
  3681. * {:(}
  3682. * </pre>
  3683. * @subsection lists Lists
  3684. *
  3685. * Textile also supports ordered and unordered lists.
  3686. * You simply place an asterisk or pound sign, followed
  3687. * with a space at the start of your lines.
  3688. *
  3689. * Simple lists:
  3690. * <pre>
  3691. * * one
  3692. * * two
  3693. * * three
  3694. * </pre>
  3695. * Multi-level lists:
  3696. * <pre>
  3697. * * one
  3698. * ** one A
  3699. * ** one B
  3700. * *** one B1
  3701. * * two
  3702. * ** two A
  3703. * ** two B
  3704. * * three
  3705. * </pre>
  3706. * Ordered lists:
  3707. * <pre>
  3708. * # one
  3709. * # two
  3710. * # three
  3711. * </pre>
  3712. * Styling lists:
  3713. * <pre>
  3714. * (class#id)* one
  3715. * * two
  3716. * * three
  3717. * </pre>
  3718. * The above sets the class and id attributes for the \<ul\>
  3719. * tag.
  3720. * <pre>
  3721. * *(class#id) one
  3722. * * two
  3723. * * three
  3724. * </pre>
  3725. * The above sets the class and id attributes for the first \<li\>
  3726. * tag.
  3727. *
  3728. * Definition lists:
  3729. * <pre>
  3730. * dl. textile:a cloth, especially one manufactured by weaving
  3731. * or knitting; a fabric
  3732. * format:the arrangement of data for storage or display.
  3733. * </pre>
  3734. * Note that there is no space between the term and definition. The
  3735. * term must be at the start of the line (or following the "dl"
  3736. * signature as shown above).
  3737. *
  3738. * @subsection tables Tables
  3739. *
  3740. * Textile supports tables. Tables must be in their own block and
  3741. * must have pipe characters delimiting the columns. An optional
  3742. * block signature of "table" may be used, usually for applying
  3743. * style, class, id or other options to the table element itself.
  3744. *
  3745. * From the simple:
  3746. * <pre>
  3747. * |a|b|c|
  3748. * |1|2|3|
  3749. * </pre>
  3750. * To the complex:
  3751. * <pre>
  3752. * table(fig). {color:red}_|Top|Row|
  3753. * {color:blue}|/2. Second|Row|
  3754. * |_{color:green}. Last|
  3755. * </pre>
  3756. * Modifiers can be specified for the table signature itself,
  3757. * for a table row (prior to the first '|' character) and
  3758. * for any cell (following the '|' for that cell). Note that for
  3759. * cells, a period followed with a space must be placed after
  3760. * any modifiers to distinguish the modifier from the cell content.
  3761. *
  3762. * Modifiers allowed are:
  3763. *
  3764. * <ul>
  3765. *
  3766. * <li><b><code>{style rule}</code></b>
  3767. *
  3768. * A CSS style rule.</li>
  3769. *
  3770. * <li><b><code>(class)</code> or <code>(#id)</code> or <code>(class#id)</code></b>
  3771. *
  3772. * A CSS class and/or id attribute.</li>
  3773. *
  3774. * <li><b><code>(</code> (one or more)</b>
  3775. *
  3776. * Adds 1em of padding to the left for each '(' character.</li>
  3777. *
  3778. * <li><b><code>)</code> (one or more)</b>
  3779. *
  3780. * Adds 1em of padding to the right for each ')' character.</li>
  3781. *
  3782. * <li><b><code>\<</code></b>
  3783. *
  3784. * Aligns to the left (floats to left for tables if combined with the
  3785. * ')' modifier).</li>
  3786. *
  3787. * <li><b><code>\></code></b>
  3788. *
  3789. * Aligns to the right (floats to right for tables if combined with
  3790. * the '(' modifier).</li>
  3791. *
  3792. * <li><b><code>=</code></b>
  3793. *
  3794. * Aligns to center (sets left, right margins to 'auto' for tables).</li>
  3795. *
  3796. * <li><b><code>\<\></code></b>
  3797. *
  3798. * For cells only. Justifies text.</li>
  3799. *
  3800. * <li><b><code>^</code></b>
  3801. *
  3802. * For rows and cells only. Aligns to the top.</li>
  3803. *
  3804. * <li><b><code>~</code> (tilde)</b>
  3805. *
  3806. * For rows and cells only. Aligns to the bottom.</li>
  3807. *
  3808. * <li><b><code>_</code> (underscore)</b>
  3809. *
  3810. * Can be applied to a table row or cell to indicate a header
  3811. * row or cell.</li>
  3812. *
  3813. * <li><b><code>\\2</code> or <code>\\3</code> or <code>\\4</code>, etc.</b>
  3814. *
  3815. * Used within cells to indicate a colspan of 2, 3, 4, etc. columns.
  3816. * When you see "\\", think "push forward".</li>
  3817. *
  3818. * <li><b><code>/2</code> or <code>/3</code> or <code>/4</code>, etc.</b>
  3819. *
  3820. * Used within cells to indicate a rowspan of 2, 3, 4, etc. rows.
  3821. * When you see "/", think "push downward".</li>
  3822. *
  3823. * </ul>
  3824. *
  3825. * When a cell is identified as a header cell and an alignment
  3826. * is specified, that becomes the default alignment for
  3827. * cells below it. You can always override this behavior by
  3828. * specifying an alignment for one of the lower cells.
  3829. *
  3830. * @subsection cssnotes CSS Notes
  3831. *
  3832. * When CSS is enabled (and it is by default), CSS class names
  3833. * are automatically applied in certain situations.
  3834. *
  3835. * <ul>
  3836. *
  3837. * <li>Aligning a block or span or other element to
  3838. * left, right, etc.
  3839. *
  3840. * "left" for left justified, "right" for right justified,
  3841. * "center" for centered text, "justify" for full-justified
  3842. * text.</li>
  3843. *
  3844. * <li>Aligning an image to the top or bottom
  3845. *
  3846. * "top" for top alignment, "bottom" for bottom alignment,
  3847. * "middle" for middle alignment.</li>
  3848. *
  3849. * <li>Footnotes
  3850. *
  3851. * "footnote" is applied to the paragraph tag for the
  3852. * footnote text itself. An id of "fn" plus the footnote
  3853. * number is placed on the paragraph for the footnote as
  3854. * well. For the footnote superscript tag, a class of
  3855. * "footnote" is used.</li>
  3856. *
  3857. * <li>Capped text
  3858. *
  3859. * For a series of characters that are uppercased, a
  3860. * span is placed around them with a class of "caps".</li>
  3861. *
  3862. * </ul>
  3863. *
  3864. * @subsection miscellaneous Miscellaneous
  3865. *
  3866. * Textile tries to do it's very best to ensure proper XHTML
  3867. * syntax. It will even attempt to fix errors you may introduce
  3868. * writing in HTML yourself. Unescaped '&' characters within
  3869. * URLs will be properly escaped. Singlet tags such as br, img
  3870. * and hr are checked for the '/' terminator (and it's added
  3871. * if necessary). The best way to make sure you produce valid
  3872. * XHTML with Textile is to not use any HTML markup at all--
  3873. * use the Textile syntax and let it produce the markup for you.
  3874. *
  3875. * @section license LICENSE
  3876. *
  3877. * Text::Textile is licensed under the same terms as Perl
  3878. * itself. Textile.php is licensed under the terms of the GNU General
  3879. * Public License.
  3880. *
  3881. * @section authorandcopyright AUTHOR & COPYRIGHT
  3882. *
  3883. * Text::Textile was written by Brad Choate, \<brad at bradchoate dot com\>.
  3884. * It is an adaptation of Textile, developed by Dean Allen of Textism.com.
  3885. *
  3886. * Textile.php is a PHP port of Brad Choate's Text::Textile
  3887. * (Textile.pm) Perl module.
  3888. *
  3889. * Textile.php was ported by Jim Riggs \<textile at jimandlissa dot
  3890. * com\>. Great care has been taken to leave the Perl code in much the
  3891. * same form as Textile.pm. While changes were required due to
  3892. * syntactical differences between Perl and PHP, much of the code was
  3893. * left intact (even if alternative syntax or code optimizations could
  3894. * have been made in PHP), even to the point where one can compare
  3895. * functions/subroutines side by side between the two implementations.
  3896. * This has been done to ensure compatibility, reduce the possibility
  3897. * of introducing errors, and simplify maintainance as one version or
  3898. * the other is updated.
  3899. *
  3900. * @author Jim Riggs \<textile at jimandlissa dot com\>
  3901. * @author Brad Choate \<brad at bradchoate dot com\>
  3902. * @copyright Copyright &copy; 2004 Jim Riggs and Brad Choate
  3903. * @version @(#) $Id: Textile.php,v 1.13 2005/03/21 15:26:55 jhriggs Exp $
  3904. */
  3905. ?>