A tumblelog CMS built on AJAX, PHP and MySQL.

textile.class.php 141KB

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