PageRenderTime 83ms CodeModel.GetById 46ms RepoModel.GetById 0ms app.codeStats 1ms

/saf/lib/XML/XT.php

https://github.com/lux/sitellite
PHP | 3429 lines | 2621 code | 119 blank | 689 comment | 154 complexity | f3aad1f09e949f8670da114e394b3339 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, Apache-2.0, GPL-3.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. //
  3. // +----------------------------------------------------------------------+
  4. // | Sitellite Content Management System |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 2010 Sitellite.org Community |
  7. // +----------------------------------------------------------------------+
  8. // | This software is released under the GNU GPL License. |
  9. // | Please see the accompanying file docs/LICENSE for licensing details. |
  10. // | |
  11. // | You should have received a copy of the GNU GPL License |
  12. // | along with this program; if not, visit www.sitellite.org. |
  13. // | The license text is also available at the following web site |
  14. // | address: <http://www.sitellite.org/index/license |
  15. // +----------------------------------------------------------------------+
  16. // | Authors: John Luxford <john.luxford@gmail.com> |
  17. // +----------------------------------------------------------------------+
  18. //
  19. // XT is an XML-based template engine.
  20. //
  21. $GLOBALS['loader']->import ('saf.Template.Transformation');
  22. $GLOBALS['loader']->import ('saf.XML.XT.Expression');
  23. define ('XT_DEFAULT_PREFIX', 'xt:');
  24. define ('XT_POST_PREFIX', 'xa:');
  25. /**
  26. * XT is an XML-based template engine. For reference information,
  27. * see the {@link http://www.sitellite.org/index/news-app/section.Templates Templates
  28. * category of the Sitellite.org articles}.
  29. *
  30. * Change History
  31. *
  32. * New in 1.2:
  33. * - Added support for xmlchar tags as a means of displaying HTML entities that
  34. * are not supported by the XML spec, without having to declare an HTML doctype.
  35. * For more information see http://xmlchar.sf.net/ and
  36. * http://www.w3.org/TR/REC-html40/sgml/entities.html for a list of HTML
  37. * entities.
  38. * - Added global functions as aliases of the main public methods. These simply
  39. * call the methods on a global XT object named $tpl. They are: template_xt(),
  40. * template_messy(), template_validate(), and template_wrap().
  41. * - Passed the default object to the $intl->get() calls so that {tag}-style
  42. * substitutions can be made.
  43. * - Added aliases xt:translate and xt:i18n that point to xt:intl.
  44. * - Added aliases xt:elsif that points to xt:elseif.
  45. * - Fixed a few bugs regarding xt:content and xt:replace attributes, and the
  46. * xt:condition, xt:if, xt:elseif, and xt:else tags.
  47. *
  48. * New in 1.4:
  49. * - Added an xt:cache tag which allows you to cache pieces of a template for
  50. * improved performance.
  51. * - Added an ignoreUntilLevel() method which controls the private
  52. * $_ignoreUntilLevel property.
  53. * - Added the $cacheLocation, $cacheDuration, and $cacheCount properties to
  54. * work with the new xt:cache tag.
  55. * - Added the $isHtml property to tell the xt:intl tag whether or not to add
  56. * an HTML span tag to its output.
  57. *
  58. * New in 1.6:
  59. * - Added xt:comment and xt:note tags (xt:note is an alias to xt:comment), so
  60. * that comments can be added to templates that will be retained in the
  61. * rendered output (since normal xml comments are stripped by the
  62. * xml_parse_into_struct() function). For comments that should not be
  63. * retained, please use ordinary xml comments.
  64. * - Added start, end, and length as attributes of loop iterators.
  65. * - Added _header_handler(), makeToc(), and support for automatic generation
  66. * of tables of contents for HTML content. Also added the $toc and $buildToc
  67. * properties.
  68. *
  69. * New in 1.8:
  70. * - Added the ability to call XT tags (attributes too? not sure) using
  71. * <xt-tagname /> as well as with the <xt:tagname /> namespace. This feature
  72. * helps when you want to render XT tags with CSS, since namespaces in CSS are
  73. * not supported by any browser (except Opera 7 apparently) at present.
  74. *
  75. * New in 2.0:
  76. * - Added the ability to use inline expressions in any tag attribute, for example:
  77. * <a href="${site/prefix}/index/news"><xt:intl>News</xt:intl></a>
  78. * This drastically reduces the amount of code needed to implement common
  79. * expressions, and really increases the flexibility of the language.
  80. * - Fixed a bug where XT string expressions that began with an inline expression
  81. * (for example: <h1 xt:content="string: ${site/domain} - welcome">welcome</h1>)
  82. * would cause the inline expression to disappear.
  83. * - Changes to PHPShorthand have improved the stability of the php expression type,
  84. * especially in the area of respecting quoted strings.
  85. *
  86. * New in 2.2:
  87. * - Fixed a bug where tags that contained no children and used the xt:condition
  88. * attribute would improperly set the $ignoreUntilLevel variable, causing
  89. * unpredictable rendering below.
  90. *
  91. * New in 2.4:
  92. * - Added a new include type "virtual" which includes a relative URL from the
  93. * web site document root, allowing the inclusion of CGI scripts and other
  94. * types of dynamic content directly into the template. This would be the
  95. * equivalent of the PHP code:
  96. *
  97. * include ('http://www.example.com/cgi-bin/script_name.cgi');
  98. *
  99. * <code>
  100. * <?php
  101. *
  102. * loader_import ('saf.XML.XT');
  103. *
  104. * $tpl = new XT ('inc/html', XT_DEFAULT_PREFIX);
  105. *
  106. * $tpldata = '';
  107. *
  108. * if ($tpl->validate ($tpldata)) {
  109. * echo $tpl->fill (
  110. * $tpldata,
  111. * array (
  112. * 'foo' => 'Testing...',
  113. * )
  114. * );
  115. * } else {
  116. * echo 'Error: ' $tpl->error . ' on line ' . $tpl->err_line;
  117. * }
  118. *
  119. * ? >
  120. * </code>
  121. *
  122. * @package XML
  123. * @author John Luxford <john.luxford@gmail.com>
  124. * @license http://www.sitellite.org/index/license GNU GPL License
  125. * @version 2.2, 2003-09-28, $Id: XT.php,v 1.17 2008/03/09 18:46:06 lux Exp $
  126. * @access public
  127. *
  128. */
  129. class XT {
  130. /**
  131. * The path to the template directory.
  132. *
  133. * @access public
  134. *
  135. */
  136. var $path = '';
  137. /**
  138. * The output of the current fill() call.
  139. *
  140. * @access public
  141. *
  142. */
  143. var $output = '';
  144. //var $object;
  145. /**
  146. * A cache for templates read from files, so if they are
  147. * called a second or third time XT doesn't have to read them from
  148. * the file system again.
  149. *
  150. * @access private
  151. *
  152. */
  153. var $cache = array ();
  154. /**
  155. * A cache of the node array of parsed templates. Used to
  156. * reduce the number of XML parsers that need to be executed during
  157. * a template with loops and complex structures in it.
  158. *
  159. * @access private
  160. *
  161. */
  162. var $nodeCache = array ();
  163. /**
  164. * An internal structure built to buffer SQL command blocks
  165. * prior to executing them.
  166. *
  167. * @access private
  168. *
  169. */
  170. var $sql = array ();
  171. //var $log = array ();
  172. /**
  173. * An internal structure built to buffer loop command blocks
  174. * prior to executing them.
  175. *
  176. * @access private
  177. *
  178. */
  179. var $loop = array ();
  180. /**
  181. * An internal structure built to buffer condition command blocks
  182. * prior to executing them.
  183. *
  184. * @access private
  185. *
  186. */
  187. var $if = array ();
  188. //var $switch = array ();
  189. /**
  190. * An internal structure built to store template blocks
  191. * so that they can be reused later in the same or even another
  192. * script (via an include call).
  193. *
  194. * @access private
  195. *
  196. */
  197. var $block = array ();
  198. //var $grids = array ();
  199. /**
  200. * Tell XT to stop output until the closing of a certain
  201. * tag has been found. This is managed via the ignoreUntilLevel()
  202. * method.
  203. *
  204. * @access private
  205. *
  206. */
  207. var $_ignoreUntilLevel = array ();
  208. /**
  209. * Used by the condition loop and condition blocks to tell
  210. * XT to continue buffering the loop or condition body until the
  211. * proper closing tag has been found.
  212. *
  213. * @access private
  214. *
  215. */
  216. var $openUntilLevel = false;
  217. /**
  218. * Used to distinguish between xt:loop and xt:condition
  219. * attributes by the $openUntilLevel logic.
  220. *
  221. * @access private
  222. *
  223. */
  224. var $isLoop = false;
  225. /**
  226. * The current XML namespace XT is looking for to find
  227. * command tags and attributes.
  228. *
  229. * @access public
  230. *
  231. */
  232. var $prefix = 'xt:';
  233. //var $postfix = 'xa:';
  234. /**
  235. * Determines whether the template should be treated as
  236. * HTML or a different kind of markup. This affects tags such
  237. * as xt:intl where an HTML span tag can be added to surround
  238. * the string.
  239. *
  240. * @access public
  241. *
  242. */
  243. var $isHtml = true;
  244. /**
  245. * Contains a list of tags that are self-closing (ie.
  246. * they do not contain any data, such as a br tag).
  247. * This list is only referenced if $isHtml is true.
  248. *
  249. * @access public
  250. *
  251. */
  252. var $selfClosing = array (
  253. 'img',
  254. 'br',
  255. 'hr',
  256. 'meta',
  257. 'link',
  258. 'area',
  259. );
  260. /**
  261. * Determines whether the template should build a
  262. * table of contents (TOC) based on HTML header tags found within.
  263. * Defaults to false, and must be set to true in order to
  264. * generate TOCs.
  265. *
  266. * @access public
  267. *
  268. */
  269. var $buildToc = false;
  270. /**
  271. * The list of HTML headers found in the document.
  272. *
  273. * @access public
  274. *
  275. */
  276. var $toc = array ();
  277. var $_addToHeader = false;
  278. var $_bind_list = array ();
  279. var $_bind_attrs = array ();
  280. var $_bind_parts = array ();
  281. /**
  282. * Location to store cached contents in. Defaults to
  283. * 'store:cache/templates/'. Note that the scope will be
  284. * appended to the $cacheLocation for each cacheable element.
  285. *
  286. * @access public
  287. *
  288. */
  289. var $cacheLocation = 'store:cache/templates/';
  290. /**
  291. * How long in seconds to store the cached elements
  292. * before regenerating them. May be overridden with the duration
  293. * attribute of the cache tag. Defaults to 3600 which is one hour.
  294. *
  295. * @access public
  296. *
  297. */
  298. var $cacheDuration = 3600;
  299. /**
  300. * Used to generate an auto-incrementing ID value for
  301. * cache elements that are missing an "id" attribute (so as
  302. * to make the attribute optional).
  303. *
  304. * @access public
  305. *
  306. */
  307. var $cacheCount = 0;
  308. /**
  309. * Contains the name of the file currently being processed.
  310. * This is set in the getDoc() method, so technically it will set
  311. * the current file even when you're just retrieving its contents.
  312. * This shouldn't affect its validity for most uses, but when you
  313. * want to retrieve the last parsed file, it means you have to do
  314. * so prior to calling getDoc() again, either directly or indirectly.
  315. *
  316. * @access public
  317. *
  318. */
  319. var $file = false;
  320. /**
  321. * The XTE object used to evaluate expressions in XT
  322. * tags.
  323. *
  324. * @access public
  325. *
  326. */
  327. var $exp;
  328. /**
  329. * The XML parser resource.
  330. *
  331. * @access private
  332. *
  333. */
  334. var $parser;
  335. /**
  336. * The error message, if an error occurs.
  337. *
  338. * @access public
  339. *
  340. */
  341. var $error;
  342. /**
  343. * The error code, if an error occurs.
  344. *
  345. * @access public
  346. *
  347. */
  348. var $err_code;
  349. /**
  350. * The error byte number, if an error occurs.
  351. *
  352. * @access public
  353. *
  354. */
  355. var $err_byte;
  356. /**
  357. * The error line number, if an error occurs.
  358. *
  359. * @access public
  360. *
  361. */
  362. var $err_line;
  363. /**
  364. * The error column number, if an error occurs.
  365. *
  366. * @access public
  367. *
  368. */
  369. var $err_colnum;
  370. /**
  371. * Rows from an xt:sql statement.
  372. *
  373. * @access public
  374. *
  375. */
  376. var $rows = 0;
  377. /**
  378. * Transformations to perform on a variable.
  379. *
  380. * @access public
  381. *
  382. */
  383. var $transformations = array ();
  384. /**
  385. * Constructor method. $prefix is either XT_DEFAULT_PREFIX,
  386. * XT_POST_PREFIX, or a custom prefix. The prefix is essentially
  387. * the XML namespace XT is to recognize. The use of multiple
  388. * namespaces can allow you to partially parse a template, cache
  389. * that, then parse the rest which might contain user-specific content
  390. * such as personal information. XT_DEFAULT_PREFIX and XT_POST_PREFIX
  391. * are constants defined by this package.
  392. *
  393. * @access public
  394. * @param string $path
  395. * @param string $prefix
  396. *
  397. */
  398. function XT ($path = '', $prefix = XT_DEFAULT_PREFIX) {
  399. $this->path = $path;
  400. $this->prefix = $prefix;
  401. $this->exp = new XTExpression (false);
  402. }
  403. /**
  404. * Retrieves a template from the appropriate location,
  405. * such as the $cache array, $nodeCache array, a file, or if
  406. * the string is the template itself, it returns that. Also
  407. * handles caching to $cache and $nodeCache of the appropriate
  408. * templates.
  409. *
  410. * @access private
  411. * @param string $data
  412. * @return string
  413. *
  414. */
  415. function getDoc ($data) {
  416. if (@is_array ($this->nodeCache[$data])) {
  417. return $this->nodeCache[$data];
  418. }
  419. $doc = $data;
  420. /*
  421. if (! empty ($this->path)) {
  422. $path = $this->path . '/';
  423. }
  424. */
  425. $path = $this->path () . '/';
  426. // get real data if data is from a file
  427. if (@is_file ($path . $data)) {
  428. if (isset ($this->cache[$data])) {
  429. $data = $this->cache[$data];
  430. } else {
  431. $file = $data;
  432. $data = @join ('', @file ($path . $file));
  433. $this->cache[$file] = $data;
  434. $this->file = $file;
  435. } // else do nothing, data is a string already
  436. }
  437. return $data;
  438. }
  439. /**
  440. * Returns either the contents of the $path property,
  441. * or the current working directory, which should be used as
  442. * the path instead.
  443. *
  444. * @access public
  445. * @return string
  446. *
  447. */
  448. function path () {
  449. if (! empty ($this->path)) {
  450. return $this->path;
  451. } else {
  452. return getcwd ();
  453. }
  454. }
  455. function ignoreUntilLevel ($level = false) {
  456. if ($level === -1) {
  457. array_pop ($this->_ignoreUntilLevel);
  458. } elseif ($level !== false) {
  459. $this->_ignoreUntilLevel[] = $level;
  460. } else {
  461. $c = count ($this->_ignoreUntilLevel);
  462. if ($c > 0) {
  463. return $this->_ignoreUntilLevel[$c - 1];
  464. } else {
  465. return false;
  466. }
  467. }
  468. }
  469. /**
  470. * Validates a template to see if it is a valid XML
  471. * document.
  472. *
  473. * @access public
  474. * @param string $data
  475. * @return boolean
  476. *
  477. */
  478. function validate ($data) {
  479. $data = $this->getDoc ($data);
  480. if (is_array ($data)) { // if data came from nodeCache it must be valid
  481. return true;
  482. }
  483. // create the xml parser now, and declare the handler methods
  484. $this->parser = xml_parser_create ($this->encoding);
  485. if (! $this->parser) {
  486. $this->error = 'Template Error: Failed to create an XML parser!';
  487. return false;
  488. }
  489. if (! xml_parser_set_option ($this->parser, XML_OPTION_CASE_FOLDING, false)) {
  490. xml_parser_free ($this->parser);
  491. $this->error = 'Template Error: Failed to disable case folding!';
  492. return false;
  493. }
  494. if ($this->parser) {
  495. if (xml_parse ($this->parser, $data, true)) {
  496. xml_parser_free ($this->parser);
  497. return true;
  498. } else {
  499. $this->err_code = xml_get_error_code ($this->parser);
  500. $this->err_line = xml_get_current_line_number ($this->parser);
  501. $this->err_byte = xml_get_current_byte_index ($this->parser);
  502. $this->err_colnum = xml_get_current_column_number ($this->parser);
  503. $this->error = 'Template Error: ' . xml_error_string ($this->err_code);
  504. xml_parser_free ($this->parser);
  505. return false;
  506. }
  507. } else {
  508. $this->error = 'Template Error: No parser available!';
  509. return false;
  510. }
  511. }
  512. /**
  513. * Executes a template. $obj is an optional object you
  514. * can pass to the template, which makes its properties immediately
  515. * available to the template. $carry is used internally to determine
  516. * whether to reset the object register before executing.
  517. *
  518. * @access public
  519. * @param string $data
  520. * @param object $obj
  521. * @param boolean $carry
  522. * @return string
  523. *
  524. */
  525. function fill ($data, $obj = '', $carry = false) {
  526. $this->error = false;
  527. // duplicate object for parser isolation
  528. $tpl = clone ($this); // deliberate copy, we want two separate objects here
  529. $tpl->exp = clone ($this->exp);
  530. if (! $carry) {
  531. //$tpl->register = array ();
  532. $tpl->exp->resetRegister ();
  533. $tpl->carry = false;
  534. } else {
  535. $tpl->carry = true;
  536. //$tpl->register = array ();
  537. //$tpl->register =& $this->register;
  538. //$this->exp->resetRegister ();
  539. }
  540. $tpl->output = '';
  541. if ($obj !== '') {
  542. //$tpl->register['object'] = $obj;
  543. $tpl->exp->setObject ($obj);
  544. } else {
  545. //$tpl->register['object'] = new StdClass;
  546. $tpl->exp->setObject (new StdClass);
  547. }
  548. $tpl->sql = array ();
  549. $tpl->loop = array ();
  550. $tpl->if = array ();
  551. $tpl->switch = array ();
  552. $tpl->buffer = array ();
  553. $tpl->open = false;
  554. $tpl->open_var = false;
  555. $tpl->toc = array ();
  556. $tpl->rows = 0;
  557. $tpl->error = false;
  558. $tpl->err_code = false;
  559. $tpl->err_byte = false;
  560. $tpl->err_line = false;
  561. $tpl->err_colnum = false;
  562. $doc = $data;
  563. $data = $tpl->getDoc ($data);
  564. if (is_array ($data)) { // use nodeCache instead of new xml parser
  565. foreach ($data as $node) {
  566. $node = $this->reverseEntities ($node);
  567. $tpl->_output ($tpl->{$tpl->makeMethod ($node['tag'], $node['type'], $node['level'])} ($node));
  568. }
  569. // gather blocks from included templates
  570. foreach ($tpl->block as $key => $block) {
  571. if (! isset ($this->block[$key])) {
  572. $this->block[$key] =& $tpl->block[$key];
  573. }
  574. }
  575. $this->rows = $tpl->rows;
  576. $this->toc = $tpl->toc;
  577. //$tpl->output = $this->reverseEntities ($tpl->output);
  578. return $tpl->output;
  579. }
  580. // create the xml parser now, and declare the handler methods
  581. $this->parser = xml_parser_create ($this->encoding);
  582. if (! $this->parser) {
  583. $this->error = 'Template Error: Failed to create an XML parser!';
  584. return false;
  585. }
  586. if (! xml_parser_set_option ($this->parser, XML_OPTION_CASE_FOLDING, false)) {
  587. xml_parser_free ($this->parser);
  588. $this->error = 'Template Error: Failed to disable case folding!';
  589. return false;
  590. }
  591. if ($this->parser) {
  592. // turning inline PHP off for the time being
  593. // actually, i don't think we need it at all
  594. //$data = $tpl->inline ($data);
  595. $data = $this->convertEntities ($data);
  596. if (xml_parse_into_struct ($this->parser, $data, $tpl->vals, $tpl->tags)) {
  597. xml_parser_free ($this->parser);
  598. //echo '<pre>';
  599. //print_r ($tpl->vals);
  600. //echo '</pre>';
  601. // cache the node structure
  602. $this->nodeCache[$data] = $tpl->vals;
  603. // list of paths for the current tag and its parents
  604. // takes the form [level] = [path 1, path 2]
  605. $this->_path_list = array ();
  606. // the current level
  607. $this->_path_level = 0;
  608. /*
  609. $colours = array (
  610. '000',
  611. '600',
  612. '060',
  613. '006',
  614. '900',
  615. '396',
  616. '369',
  617. 'f00',
  618. '0f0',
  619. '00f',
  620. 'f90',
  621. '666',
  622. '999',
  623. 'bbb',
  624. );
  625. */
  626. // mainloop
  627. foreach ($tpl->vals as $node) {
  628. $node = $this->reverseEntities ($node);
  629. $norm_tag = str_replace (':', '-', $node['tag']);
  630. if ($node['type'] == 'cdata' || strpos ($norm_tag, 'ch-') === 0 || strpos ($norm_tag, 'xt-') === 0 || ! in_array ($norm_tag, $tpl->_bind_parts)) {
  631. $tpl->_output ($tpl->{$tpl->makeMethod ($node['tag'], $node['type'], $node['level'])} ($node));
  632. continue;
  633. }
  634. // echo '<span style="color: #' . $colours[$node['level']] . '">' . str_repeat (' ', $node['level']) . $node['tag'] . ' (' . $node['type'] . ")</span>\n";
  635. $node['paths'] = array ();
  636. if ($node['type'] == 'open' || $node['type'] == 'complete') {
  637. if ($node['level'] > $this->_path_level) {
  638. // moving up a new level (ie. a sub-node)
  639. $this->_path_level++;
  640. $this->_path_list[$this->_path_level] = $node;
  641. } elseif ($this->_path_level > 0 && $node['level'] == $this->_path_level) {
  642. // next sibling at the same level
  643. array_pop ($this->_path_list);
  644. $this->_path_list[$this->_path_level] = $node;
  645. } elseif ($node['level'] < $this->_path_level) {
  646. // do nothing...
  647. } else {
  648. // moving up a new level
  649. $this->_path_level++;
  650. $this->_path_list[$this->_path_level] = $node;
  651. }
  652. // compile all variations of this tag's xpath for a match in $this->_bind_list
  653. $paths = array ('//' . $node['tag']);
  654. $list = $this->_path_list[$this->_path_level - 2]['paths'];
  655. if (is_array ($list)) {
  656. foreach ($list as $p) {
  657. $paths[] = $p . '/' . $node['tag'];
  658. $paths[] = $p . '//' . $node['tag'];
  659. }
  660. } else {
  661. $paths[] = '/' . $node['tag'];
  662. }
  663. $count = count ($paths);
  664. $cpl = count ($this->_path_list) - 1;
  665. if (is_array ($this->_path_list[$cpl]['attributes'])) {
  666. foreach ($this->_path_list[$cpl]['attributes'] as $k => $v) {
  667. if (strpos ($k, 'xt:') !== 0) {
  668. for ($i = 0; $i < $count; $i++) {
  669. $paths[] = $paths[$i] . '[@' . $k . '="' . $v . '"]';
  670. }
  671. }
  672. }
  673. }
  674. // echo '<div style="padding: 10px; margin: 10px; border: 1px solid #aaa">' . join ("\n", $paths) . '</div>';
  675. $this->_path_list[$cpl]['paths'] = $paths;
  676. $node['paths'] = $paths;
  677. if ($node['type'] == 'complete') {
  678. foreach (array_intersect (array_keys ($this->_bind_list), $paths) as $key) {
  679. $node['value'] .= $this->_bind_list[$key];
  680. }
  681. }
  682. foreach (array_intersect (array_keys ($this->_bind_attrs), $paths) as $key) {
  683. //info ($node['attributes']);
  684. foreach ($this->_bind_attrs[$key] as $k => $v) {
  685. $node['attributes'][$k] = $v;
  686. }
  687. //info ($node['attributes']);
  688. }
  689. if ($node['type'] == 'complete') {
  690. $this->_path_level--;
  691. array_pop ($this->_path_list);
  692. }
  693. } elseif ($node['type'] == 'close') {
  694. if (count ($this->_path_list) > 0 && $this->_path_list[count ($this->_path_list) - 1] != null) {
  695. foreach (array_intersect (array_keys ($this->_bind_list), $this->_path_list[count ($this->_path_list) - 1]['paths']) as $key) {
  696. $tpl->_output ($this->_bind_list[$key]);
  697. }
  698. $this->_path_level--;
  699. array_pop ($this->_path_list);
  700. }
  701. }
  702. $tpl->_output ($tpl->{$tpl->makeMethod ($node['tag'], $node['type'], $node['level'])} ($node));
  703. }
  704. // gather blocks from included templates
  705. foreach ($tpl->block as $key => $block) {
  706. if (! isset ($this->block[$key])) {
  707. $this->block[$key] =& $tpl->block[$key];
  708. }
  709. }
  710. $this->rows = $tpl->rows;
  711. $this->toc = $tpl->toc;
  712. //$tpl->output = $this->reverseEntities ($tpl->output);
  713. return $tpl->output;
  714. } else {
  715. $this->err_code = xml_get_error_code ($this->parser);
  716. $this->err_line = xml_get_current_line_number ($this->parser);
  717. $this->err_byte = xml_get_current_byte_index ($this->parser);
  718. $this->err_colnum = xml_get_current_column_number ($this->parser);
  719. $this->error = 'Template Error: ' . xml_error_string ($this->err_code);
  720. xml_parser_free ($this->parser);
  721. return false;
  722. }
  723. } else {
  724. $this->error = 'Template Error: No parser available!';
  725. return false;
  726. }
  727. }
  728. /**
  729. * Bind some content to the specified tag.
  730. *
  731. * @access public
  732. * @param string
  733. * @param string
  734. *
  735. */
  736. function bind ($path, $data) {
  737. $this->_bind_list[$path] .= $data;
  738. $path = preg_split ('|/+|', $path, -1, PREG_SPLIT_NO_EMPTY);
  739. foreach ($path as $k => $v) {
  740. if (strpos ($v, '[') !== false) {
  741. $path[$k] = preg_replace ('|\[[^\]]+\]|', '', $v);
  742. }
  743. }
  744. $this->_bind_parts = array_unique (array_merge ($this->_bind_parts, $path));
  745. }
  746. /**
  747. * Bind an attribute to the specified tag.
  748. *
  749. * @access public
  750. * @param string
  751. * @param string
  752. * @param string
  753. *
  754. */
  755. function bindAttr ($path, $attr, $value) {
  756. $this->_bind_attrs[$path][$attr] = $value;
  757. $path = preg_split ('|/+|', $path, -1, PREG_SPLIT_NO_EMPTY);
  758. foreach ($path as $k => $v) {
  759. if (strpos ($v, '[') !== false) {
  760. $path[$k] = preg_replace ('|\[[^\]]+\]|', '', $v);
  761. }
  762. }
  763. $this->_bind_parts = array_unique (array_merge ($this->_bind_parts, $path));
  764. }
  765. /**
  766. * Uses the saf.HTML.Messy package to implement a "messy"
  767. * parser in XT, allowing for invalid markup in templates (ie.
  768. * HTML instead of XHTML). Of course since the markup accepted is
  769. * invalid, your mileage may vary. This method is discouraged
  770. * unless you have a good reason for using it.
  771. *
  772. * @access public
  773. * @param string $data
  774. * @param object $obj
  775. * @param boolean $carry
  776. * @return string
  777. *
  778. */
  779. function messy ($data, $obj = '', $carry = false) {
  780. if (! is_object ($this->messy)) {
  781. global $loader;
  782. $loader->import ('saf.HTML.Messy');
  783. $this->messy = new Messy ();
  784. }
  785. /*
  786. if (! empty ($this->path)) {
  787. $path = $this->path . '/';
  788. }
  789. */
  790. $path = $this->path () . '/';
  791. if (@is_file ($path . $data)) {
  792. $data = $this->getDoc ($data);
  793. }
  794. $this->nodeCache[$data] = $this->messy->parse ($data);
  795. return $this->fill ($data, $obj, $carry);
  796. }
  797. /**
  798. * Executes the specified box using the Sitellite box API,
  799. * which is essentially just an include. Note: This is now an alias
  800. * for the loader_box() function.
  801. *
  802. * @access public
  803. * @param string $name
  804. * @param associative array $parameters
  805. * @return string
  806. *
  807. */
  808. function box ($name, $parameters = array ()) {
  809. if ($this->file) {
  810. $GLOBALS['_xte'] =& $this->exp;
  811. }
  812. $out = loader_box ($name, $parameters);
  813. unset ($GLOBALS['_xte']);
  814. if (empty ($out)) {
  815. return html_marker ('Empty Box: ' . $name);
  816. }
  817. return html_marker ('Box: ' . $name) . $out;
  818. }
  819. /**
  820. * Executes the specified form using the Sitellite form API,
  821. * which is essentially just an include of a file that defines a
  822. * subclass of saf.MailForm. Note: This is now an alias
  823. * for the loader_form() function.
  824. *
  825. * @access public
  826. * @param string $name
  827. * @return string
  828. *
  829. */
  830. function form ($name) {
  831. $out = loader_form ($name);
  832. if (empty ($out)) {
  833. return html_marker ('Empty Form: ' . $name);
  834. }
  835. return html_marker ('Form: ' . $name) . $out;
  836. }
  837. /**
  838. * Evaluates PHP code embedded into a template. Currently
  839. * not used, because there's really no reason why embedded PHP
  840. * should be needed.
  841. *
  842. * @access private
  843. * @param string $data
  844. * @return string
  845. *
  846. */
  847. function inline ($data) {
  848. ob_start ();
  849. eval (CLOSE_TAG . $data);
  850. $newdata = ob_get_contents ();
  851. ob_end_clean ();
  852. return $newdata;
  853. }
  854. /**
  855. * Determines which callback function to call for the
  856. * specified node.
  857. *
  858. * @access private
  859. * @param string $name
  860. * @param string $type
  861. * @return string
  862. *
  863. */
  864. function makeMethod ($name, $type, $level) {
  865. $iul = $this->ignoreUntilLevel ();
  866. if ($iul && $iul < $level) {
  867. switch ($type) {
  868. case 'close':
  869. return '_default_end';
  870. case 'cdata':
  871. return '_default_cdata';
  872. default:
  873. return '_default';
  874. }
  875. }
  876. if (strpos ($name, 'xt-') === 0) {
  877. $name = str_replace ('xt-', 'xt:', $name);
  878. }
  879. switch ($type) {
  880. case 'complete':
  881. if (strpos ($name, 'ch:') === 0) {
  882. return '_ch_handler';
  883. }
  884. case 'open':
  885. if ($this->buildToc && in_array ($name, array ('h1', 'h2', 'h3', 'h4', 'h5', 'h6'))) {
  886. return '_header_handler';
  887. }
  888. $m = str_replace (array ($this->prefix, '-'), array ('_', '_'), $name);
  889. $d = '_default';
  890. break;
  891. case 'close':
  892. if ($this->buildToc && in_array ($name, array ('h1', 'h2', 'h3', 'h4', 'h5', 'h6'))) {
  893. return '_header_end';
  894. }
  895. $m = str_replace (array ($this->prefix, '-'), array ('_', '_'), $name) . '_end';
  896. $d = '_default_end';
  897. break;
  898. default:
  899. $m = str_replace (array ($this->prefix, '-'), array ('_', '_'), $name) . '_cdata';
  900. $d = '_default_cdata';
  901. }
  902. if (strpos ($m, '_') === 0 && method_exists ($this, $m)) {
  903. return $m;
  904. }
  905. return $d;
  906. }
  907. /**
  908. * Determines where to send the output of a tag callback.
  909. *
  910. * @access private
  911. * @param string $str
  912. *
  913. */
  914. function _output ($str) {
  915. if ($this->open) {
  916. $this->open_var .= $str;
  917. } else {
  918. $this->output .= $str;
  919. }
  920. }
  921. /**
  922. * Wraps $str in xt:tpl tags and returns it.
  923. *
  924. * @access public
  925. * @param string $str
  926. * @return string
  927. *
  928. */
  929. function wrap ($str) {
  930. return '<' . $this->prefix . 'tpl>' . $str . '</' . $this->prefix . 'tpl>';
  931. }
  932. /**
  933. * Sets the value of a property of the default object
  934. * in the register. Always returns an empty string.
  935. *
  936. * @access private
  937. * @param string $name
  938. * @param string $value
  939. * @return string
  940. *
  941. */
  942. function setVal ($name, $value) {
  943. if (is_object ($this->exp->register['object'])) {
  944. $this->exp->register['object']->{$name} = $value;
  945. } elseif (is_array ($this->exp->register['object'])) {
  946. $this->exp->register['object'][$name] = $value;
  947. } else {
  948. $this->exp->setObject (new StdClass);
  949. $this->exp->register['object']->{$name} = $value;
  950. }
  951. return '';
  952. }
  953. /**
  954. * Gets the value of a property in the register.
  955. * Returns that value, which may be transformed, if a
  956. * transformation has been defined for that property name.
  957. *
  958. * @access private
  959. * @param string $val
  960. * @param associative array $node
  961. * @param string $type
  962. * @param boolean $transform
  963. * @return string
  964. *
  965. */
  966. function getVal ($val, $node, $type = 'path', $transform = true) {
  967. if (preg_match ('/\/([a-zA-Z0-9_-]+)$/', $val, $regs)) {
  968. $var = $regs[1];
  969. } else {
  970. $var = $val;
  971. }
  972. $val = $this->exp->evaluate ($val, $node, $type, $this->carry);
  973. if ($transform && isset ($this->transformations[$var]) && is_object ($this->transformations[$var])) {
  974. return $this->transformations[$var]->transform ($val);
  975. } else {
  976. return $val;
  977. }
  978. }
  979. /**
  980. * Reverses the conversion of HTML entities to XT-compatible ch:entity
  981. * tags.
  982. *
  983. * @access public
  984. * @param string $data
  985. * @return string
  986. *
  987. */
  988. function reverseEntities ($data) {
  989. if (is_array ($data)) {
  990. if (isset ($data['value'])) {
  991. $data['value'] = $this->reverseEntities ($data['value']);
  992. }
  993. if (isset ($data['attributes'])) {
  994. foreach ($data['attributes'] as $key => $value) {
  995. $data['attributes'][$key] = $this->reverseEntities ($value);
  996. }
  997. }
  998. return $data;
  999. }
  1000. $data = preg_replace (
  1001. '/\[ch:n([0-9]+)\]/',
  1002. '&#\1;',
  1003. $data
  1004. );
  1005. return preg_replace (
  1006. '/\[ch:([a-zA-Z0-9]+)\]/',
  1007. '&\1;',
  1008. $data
  1009. );
  1010. }
  1011. /**
  1012. * Converts HTML entities into XT-compatible ch:entity
  1013. * tags.
  1014. *
  1015. * @access public
  1016. * @param string $data
  1017. * @return string
  1018. *
  1019. */
  1020. function convertEntities ($data) {
  1021. $data = preg_replace (
  1022. '/&#([0-9]+);/',
  1023. '[ch:n\1]',
  1024. $data
  1025. );
  1026. return preg_replace (
  1027. '/&([a-zA-Z0-9]+);/',
  1028. '[ch:\1]',
  1029. $data
  1030. );
  1031. $data = preg_replace (
  1032. '/&#([0-9]+);/',
  1033. '<ch:n\1 />',
  1034. $data
  1035. );
  1036. return preg_replace (
  1037. '/&([a-zA-Z0-9]+);/',
  1038. '<ch:\1 />',
  1039. $data
  1040. );
  1041. return str_replace (
  1042. array (
  1043. '&nbsp;',
  1044. '&iexcl;',
  1045. '&cent;',
  1046. '&pound;',
  1047. '&curren;',
  1048. '&yen;',
  1049. '&brvbar;',
  1050. '&sect;',
  1051. '&uml;',
  1052. '&copy;',
  1053. '&ordf;',
  1054. '&laquo;',
  1055. '&not;',
  1056. '&shy;',
  1057. '&reg;',
  1058. '&macr;',
  1059. '&deg;',
  1060. '&plusmn;',
  1061. '&sup2;',
  1062. '&sup3;',
  1063. '&acute;',
  1064. '&micro;',
  1065. '&para;',
  1066. '&middot;',
  1067. '&cedil;',
  1068. '&sup1;',
  1069. '&ordm;',
  1070. '&raquo;',
  1071. '&frac14;',
  1072. '&frac12;',
  1073. '&frac34;',
  1074. '&iquest;',
  1075. '&Agrave;',
  1076. '&Aacute;',
  1077. '&Acirc;',
  1078. '&Atilde;',
  1079. '&Auml;',
  1080. '&Aring;',
  1081. '&AElig;',
  1082. '&Ccedil;',
  1083. '&Egrave;',
  1084. '&Eacute;',
  1085. '&Ecirc;',
  1086. '&Euml;',
  1087. '&Igrave;',
  1088. '&Iacute;',
  1089. '&Icirc;',
  1090. '&Iuml;',
  1091. '&ETH;',
  1092. '&Ntilde;',
  1093. '&Ograve;',
  1094. '&Oacute;',
  1095. '&Ocirc;',
  1096. '&Otilde;',
  1097. '&Ouml;',
  1098. '&times;',
  1099. '&Oslash;',
  1100. '&Ugrave;',
  1101. '&Uacute;',
  1102. '&Ucirc;',
  1103. '&Uuml;',
  1104. '&Yacute;',
  1105. '&THORN;',
  1106. '&szlig;',
  1107. '&agrave;',
  1108. '&aacute;',
  1109. '&acirc;',
  1110. '&atilde;',
  1111. '&auml;',
  1112. '&aring;',
  1113. '&aelig;',
  1114. '&ccedil;',
  1115. '&egrave;',
  1116. '&eacute;',
  1117. '&ecirc;',
  1118. '&euml;',
  1119. '&igrave;',
  1120. '&iacute;',
  1121. '&icirc;',
  1122. '&iuml;',
  1123. '&eth;',
  1124. '&ntilde;',
  1125. '&ograve;',
  1126. '&oacute;',
  1127. '&ocirc;',
  1128. '&otilde;',
  1129. '&ouml;',
  1130. '&divide;',
  1131. '&oslash;',
  1132. '&ugrave;',
  1133. '&uacute;',
  1134. '&ucirc;',
  1135. '&uuml;',
  1136. '&yacute;',
  1137. '&thorn;',
  1138. '&yuml;',
  1139. '&fnof;',
  1140. '&Alpha;',
  1141. '&Beta;',
  1142. '&Gamma;',
  1143. '&Delta;',
  1144. '&Epsilon;',
  1145. '&Zeta;',
  1146. '&Eta;',
  1147. '&Theta;',
  1148. '&Iota;',
  1149. '&Kappa;',
  1150. '&Lambda;',
  1151. '&Mu;',
  1152. '&Nu;',
  1153. '&Xi;',
  1154. '&Omicron;',
  1155. '&Pi;',
  1156. '&Rho;',
  1157. '&Sigma;',
  1158. '&Tau;',
  1159. '&Upsilon;',
  1160. '&Phi;',
  1161. '&Chi;',
  1162. '&Psi;',
  1163. '&Omega;',
  1164. '&alpha;',
  1165. '&beta;',
  1166. '&gamma;',
  1167. '&delta;',
  1168. '&epsilon;',
  1169. '&zeta;',
  1170. '&eta;',
  1171. '&theta;',
  1172. '&iota;',
  1173. '&kappa;',
  1174. '&lambda;',
  1175. '&mu;',
  1176. '&nu;',
  1177. '&xi;',
  1178. '&omicron;',
  1179. '&pi;',
  1180. '&rho;',
  1181. '&sigmaf;',
  1182. '&sigma;',
  1183. '&tau;',
  1184. '&upsilon;',
  1185. '&phi;',
  1186. '&chi;',
  1187. '&psi;',
  1188. '&omega;',
  1189. '&thetasym;',
  1190. '&upsih;',
  1191. '&piv;',
  1192. '&bull;',
  1193. '&hellip;',
  1194. '&prime;',
  1195. '&Prime;',
  1196. '&oline;',
  1197. '&frasl;',
  1198. '&weierp;',
  1199. '&image;',
  1200. '&real;',
  1201. '&trade;',
  1202. '&alefsym;',
  1203. '&larr;',
  1204. '&uarr;',
  1205. '&rarr;',
  1206. '&darr;',
  1207. '&harr;',
  1208. '&crarr;',
  1209. '&lArr;',
  1210. '&uArr;',
  1211. '&rArr;',
  1212. '&dArr;',
  1213. '&hArr;',
  1214. '&forall;',
  1215. '&part;',
  1216. '&exist;',
  1217. '&empty;',
  1218. '&nabla;',
  1219. '&isin;',
  1220. '&notin;',
  1221. '&ni;',
  1222. '&prod;',
  1223. '&sum;',
  1224. '&minus;',
  1225. '&lowast;',
  1226. '&radic;',
  1227. '&prop;',
  1228. '&infin;',
  1229. '&ang;',
  1230. '&and;',
  1231. '&or;',
  1232. '&cap;',
  1233. '&cup;',
  1234. '&int;',
  1235. '&there4;',
  1236. '&sim;',
  1237. '&cong;',
  1238. '&asymp;',
  1239. '&ne;',
  1240. '&equiv;',
  1241. '&le;',
  1242. '&ge;',
  1243. '&sub;',
  1244. '&sup;',
  1245. '&nsub;',
  1246. '&sube;',
  1247. '&supe;',
  1248. '&oplus;',
  1249. '&otimes;',
  1250. '&perp;',
  1251. '&sdot;',
  1252. '&lceil;',
  1253. '&rceil;',
  1254. '&lfloor;',
  1255. '&rfloor;',
  1256. '&lang;',
  1257. '&rang;',
  1258. '&loz;',
  1259. '&spades;',
  1260. '&clubs;',
  1261. '&hearts;',
  1262. '&diams;',
  1263. '&quot;',
  1264. '&amp;',
  1265. '&lt;',
  1266. '&gt;',
  1267. '&OElig;',
  1268. '&oelig;',
  1269. '&Scaron;',
  1270. '&scaron;',
  1271. '&Yuml;',
  1272. '&circ;',
  1273. '&tilde;',
  1274. '&ensp;',
  1275. '&emsp;',
  1276. '&thinsp;',
  1277. '&zwnj;',
  1278. '&zwj;',
  1279. '&lrm;',
  1280. '&rlm;',
  1281. '&ndash;',
  1282. '&mdash;',
  1283. '&lsquo;',
  1284. '&rsquo;',
  1285. '&sbquo;',
  1286. '&ldquo;',
  1287. '&rdquo;',
  1288. '&bdquo;',
  1289. '&dagger;',
  1290. '&Dagger;',
  1291. '&permil;',
  1292. '&lsaquo;',
  1293. '&rsaquo;',
  1294. '&euro;',
  1295. ),
  1296. array (
  1297. '<ch:nbsp />',
  1298. '<ch:iexcl />',
  1299. '<ch:cent />',
  1300. '<ch:pound />',
  1301. '<ch:curren />',
  1302. '<ch:yen />',
  1303. '<ch:brvbar />',
  1304. '<ch:sect />',
  1305. '<ch:uml />',
  1306. '<ch:copy />',
  1307. '<ch:ordf />',
  1308. '<ch:laquo />',
  1309. '<ch:not />',
  1310. '<ch:shy />',
  1311. '<ch:reg />',
  1312. '<ch:macr />',
  1313. '<ch:deg />',
  1314. '<ch:plusmn />',
  1315. '<ch:sup2 />',
  1316. '<ch:sup3 />',
  1317. '<ch:acute />',
  1318. '<ch:micro />',
  1319. '<ch:para />',
  1320. '<ch:middot />',
  1321. '<ch:cedil />',
  1322. '<ch:sup1 />',
  1323. '<ch:ordm />',
  1324. '<ch:raquo />',
  1325. '<ch:frac14 />',
  1326. '<ch:frac12 />',
  1327. '<ch:frac34 />',
  1328. '<ch:iquest />',
  1329. '<ch:Agrave />',
  1330. '<ch:Aacute />',
  1331. '<ch:Acirc />',
  1332. '<ch:Atilde />',
  1333. '<ch:Auml />',
  1334. '<ch:Aring />',
  1335. '<ch:AElig />',
  1336. '<ch:Ccedil />',
  1337. '<ch:Egrave />',
  1338. '<ch:Eacute />',
  1339. '<ch:Ecirc />',
  1340. '<ch:Euml />',
  1341. '<ch:Igrave />',
  1342. '<ch:Iacute />',
  1343. '<ch:Icirc />',
  1344. '<ch:Iuml />',
  1345. '<ch:ETH />',
  1346. '<ch:Ntilde />',
  1347. '<ch:Ograve />',
  1348. '<ch:Oacute />',
  1349. '<ch:Ocirc />',
  1350. '<ch:Otilde />',
  1351. '<ch:Ouml />',
  1352. '<ch:times />',
  1353. '<ch:Oslash />',
  1354. '<ch:Ugrave />',
  1355. '<ch:Uacute />',
  1356. '<ch:Ucirc />',
  1357. '<ch:Uuml />',
  1358. '<ch:Yacute />',
  1359. '<ch:THORN />',
  1360. '<ch:szlig />',
  1361. '<ch:agrave />',
  1362. '<ch:aacute />',
  1363. '<ch:acirc />',
  1364. '<ch:atilde />',
  1365. '<ch:auml />',
  1366. '<ch:aring />',
  1367. '<ch:aelig />',
  1368. '<ch:ccedil />',
  1369. '<ch:egrave />',
  1370. '<ch:eacute />',
  1371. '<ch:ecirc />',
  1372. '<ch:euml />',
  1373. '<ch:igrave />',
  1374. '<ch:iacute />',
  1375. '<ch:icirc />',
  1376. '<ch:iuml />',
  1377. '<ch:eth />',
  1378. '<ch:ntilde />',
  1379. '<ch:ograve />',
  1380. '<ch:oacute />',
  1381. '<ch:ocirc />',
  1382. '<ch:otilde />',
  1383. '<ch:ouml />',
  1384. '<ch:divide />',
  1385. '<ch:oslash />',
  1386. '<ch:ugrave />',
  1387. '<ch:uacute />',
  1388. '<ch:ucirc />',
  1389. '<ch:uuml />',
  1390. '<ch:yacute />',
  1391. '<ch:thorn />',
  1392. '<ch:yuml />',
  1393. '<ch:fnof />',
  1394. '<ch:Alpha />',
  1395. '<ch:Beta />',
  1396. '<ch:Gamma />',
  1397. '<ch:Delta />',
  1398. '<ch:Epsilon />',
  1399. '<ch:Zeta />',
  1400. '<ch:Eta />',
  1401. '<ch:Theta />',
  1402. '<ch:Iota />',
  1403. '<ch:Kappa />',
  1404. '<ch:Lambda />',
  1405. '<ch:Mu />',
  1406. '<ch:Nu />',
  1407. '<ch:Xi />',
  1408. '<ch:Omicron />',
  1409. '<ch:Pi />',
  1410. '<ch:Rho />',
  1411. '<ch:Sigma />',
  1412. '<ch:Tau />',
  1413. '<ch:Upsilon />',
  1414. '<ch:Phi />',
  1415. '<ch:Chi />',
  1416. '<ch:Psi />',
  1417. '<ch:Omega />',
  1418. '<ch:alpha />',
  1419. '<ch:beta />',
  1420. '<ch:gamma />',
  1421. '<ch:delta />',
  1422. '<ch:epsilon />',
  1423. '<ch:zeta />',
  1424. '<ch:eta />',
  1425. '<ch:theta />',
  1426. '<ch:iota />',
  1427. '<ch:kappa />',
  1428. '<ch:lambda />',
  1429. '<ch:mu />',
  1430. '<ch:nu />',
  1431. '<ch:xi />',
  1432. '<ch:omicron />',
  1433. '<ch:pi />',
  1434. '<ch:rho />',
  1435. '<ch:sigmaf />',
  1436. '<ch:sigma />',
  1437. '<ch:tau />',
  1438. '<ch:upsilon />',
  1439. '<ch:phi />',
  1440. '<ch:chi />',
  1441. '<ch:psi />',
  1442. '<ch:omega />',
  1443. '<ch:thetasym />',
  1444. '<ch:upsih />',
  1445. '<ch:piv />',
  1446. '<ch:bull />',
  1447. '<ch:hellip />',
  1448. '<ch:prime />',
  1449. '<ch:Prime />',
  1450. '<ch:oline />',
  1451. '<ch:frasl />',
  1452. '<ch:weierp />',
  1453. '<ch:image />',
  1454. '<ch:real />',
  1455. '<ch:trade />',
  1456. '<ch:alefsym />',
  1457. '<ch:larr />',
  1458. '<ch:uarr />',
  1459. '<ch:rarr />',
  1460. '<ch:darr />',
  1461. '<ch:harr />',
  1462. '<ch:crarr />',
  1463. '<ch:lArr />',
  1464. '<ch:uArr />',
  1465. '<ch:rArr />',
  1466. '<ch:dArr />',
  1467. '<ch:hArr />',
  1468. '<ch:forall />',
  1469. '<ch:part />',
  1470. '<ch:exist />',
  1471. '<ch:empty />',
  1472. '<ch:nabla />',
  1473. '<ch:isin />',
  1474. '<ch:notin />',
  1475. '<ch:ni />',
  1476. '<ch:prod />',
  1477. '<ch:sum />',
  1478. '<ch:minus />',
  1479. '<ch:lowast />',
  1480. '<ch:radic />',
  1481. '<ch:prop />',
  1482. '<ch:infin />',
  1483. '<ch:ang />',
  1484. '<ch:and />',
  1485. '<ch:or />',
  1486. '<ch:cap />',
  1487. '<ch:cup />',
  1488. '<ch:int />',
  1489. '<ch:there4 />',
  1490. '<ch:sim />',
  1491. '<ch:cong />',
  1492. '<ch:asymp />',
  1493. '<ch:ne />',
  1494. '<ch:equiv />',
  1495. '<ch:le />',
  1496. '<ch:ge />',
  1497. '<ch:sub />',
  1498. '<ch:sup />',
  1499. '<ch:nsub />',
  1500. '<ch:sube />',
  1501. '<ch:supe />',
  1502. '<ch:oplus />',
  1503. '<ch:otimes />',
  1504. '<ch:perp />',
  1505. '<ch:sdot />',
  1506. '<ch:lceil />',
  1507. '<ch:rceil />',
  1508. '<ch:lfloor />',
  1509. '<ch:rfloor />',
  1510. '<ch:lang />',
  1511. '<ch:rang />',
  1512. '<ch:loz />',
  1513. '<ch:spades />',
  1514. '<ch:clubs />',
  1515. '<ch:hearts />',
  1516. '<ch:diams />',
  1517. '<ch:quot />',
  1518. '<ch:amp />',
  1519. '<ch:lt />',
  1520. '<ch:gt />',
  1521. '<ch:OElig />',
  1522. '<ch:oelig />',
  1523. '<ch:Scaron />',
  1524. '<ch:scaron />',
  1525. '<ch:Yuml />',
  1526. '<ch:circ />',
  1527. '<ch:tilde />',
  1528. '<ch:ensp />',
  1529. '<ch:emsp />',
  1530. '<ch:thinsp />',
  1531. '<ch:zwnj />',
  1532. '<ch:zwj />',
  1533. '<ch:lrm />',
  1534. '<ch:rlm />',
  1535. '<ch:ndash />',
  1536. '<ch:mdash />',
  1537. '<ch:lsquo />',
  1538. '<ch:rsquo />',
  1539. '<ch:sbquo />',
  1540. '<ch:ldquo />',
  1541. '<ch:rdquo />',
  1542. '<ch:bdquo />',
  1543. '<ch:dagger />',
  1544. '<ch:Dagger />',
  1545. '<ch:permil />',
  1546. '<ch:lsaquo />',
  1547. '<ch:rsaquo />',
  1548. '<ch:euro />',
  1549. ),
  1550. $data
  1551. );
  1552. }
  1553. // ------------------------------
  1554. // ----- TAG HANDLERS BELOW -----
  1555. // ------------------------------
  1556. /**
  1557. * Default open and complete tag handler.
  1558. *
  1559. * @access private
  1560. * @param associative array $node
  1561. * @return string
  1562. *
  1563. */
  1564. function _default ($node) {
  1565. $iul = $this->ignoreUntilLevel ();
  1566. if ($iul && $iul < $node['level']) {
  1567. return '';
  1568. }
  1569. $out = '<' . $node['tag'];
  1570. if ($this->_addToHeader) {
  1571. $this->toc[count ($this->toc) - 1]['value'] .= $node['value'];
  1572. }
  1573. if (isset ($node['attributes'])) {
  1574. if (! $this->open) {
  1575. if (isset ($node['attributes'][$this->prefix . 'replace'])) {
  1576. $replace = $node['attributes'][$this->prefix . 'replace'];
  1577. unset ($node['attributes'][$this->prefix . 'replace']);
  1578. if ($node['type'] != 'complete') {
  1579. $this->ignoreUntilLevel ($node['level']);
  1580. }
  1581. return $this->getVal ($replace, $node, 'path', true);
  1582. } else {
  1583. if (isset ($node['attributes'][$this->prefix . 'attributes'])) {
  1584. $statements = $this->exp->splitStatement ($node['attributes'][$this->prefix . 'attributes']);
  1585. foreach ($statements as $statement) {
  1586. list ($attr, $expr) = $this->exp->splitAssignment ($statement);
  1587. $node['attributes'][$attr] = $this->exp->evaluate ($expr, $node, 'path', true);
  1588. }
  1589. unset ($node['attributes'][$this->prefix . 'attributes']);
  1590. } elseif (isset ($node['attributes'][$this->prefix . 'attrs'])) {
  1591. $statements = $this->exp->splitStatement ($node['attributes'][$this->prefix . 'attrs']);
  1592. foreach ($statements as $statement) {
  1593. list ($attr, $expr) = $this->exp->splitAssignment ($statement);
  1594. $node['attributes'][$attr] = $this->exp->evaluate ($expr, $node, 'path', true);
  1595. }
  1596. unset ($node['attributes'][$this->prefix . 'attrs']);
  1597. }
  1598. if (isset ($node['attributes'][$this->prefix . 'content'])) {
  1599. $node['value'] = $this->getVal ($node['attributes'][$this->prefix . 'content'], $node, 'path', true);
  1600. unset ($node['attributes'][$this->prefix . 'content']);
  1601. if ($node['type'] != 'complete') {
  1602. $this->ignoreUntilLevel ($node['level']);
  1603. }
  1604. }
  1605. if (isset ($node['attributes'][$this->prefix . 'loop'])) {
  1606. list ($varname, $expr) = $this->exp->splitAssignment ($node['attributes'][$this->prefix . 'loop']);
  1607. $this->loop[] = array (
  1608. 'varname' => $varname,
  1609. 'expr' => $expr,
  1610. 'struct' => '',
  1611. );
  1612. $this->open = true;
  1613. $this->open_var =& $this->loop[count ($this->loop) - 1]['struct'];
  1614. $this->openUntilLevel = $node['level'];
  1615. $this->isLoop = true;
  1616. unset ($node['attributes'][$this->prefix . 'loop']);
  1617. } elseif (isset ($node['attributes'][$this->prefix . 'repeat'])) {
  1618. list ($varname, $expr) = $this->exp->splitAssignment ($node['attributes'][$this->prefix . 'repeat']);
  1619. $this->loop[] = array (
  1620. 'varname' => $varname,
  1621. 'expr' => $expr,
  1622. 'struct' => '',
  1623. );
  1624. $this->open = true;
  1625. $this->open_var =& $this->loop[count ($this->loop) - 1]['struct'];
  1626. $this->openUntilLevel = $node['level'];
  1627. $this->isLoop = true;
  1628. unset ($node['attributes'][$this->prefix . 'repeat']);
  1629. }
  1630. if (isset ($node['attributes'][$this->prefix . 'condition'])) {
  1631. list ($one, $two) = $this->exp->splitAssignment ($node['attributes'][$this->prefix . 'condition']);
  1632. if ($one === 'not') {
  1633. $negate = true;
  1634. $expr = $two;
  1635. } else {
  1636. $negate = false;
  1637. $expr = $node['attributes'][$this->prefix . 'condition'];
  1638. }
  1639. //echo '<pre>evaluating expression "' . "\t" . $expr . "\t" . '"</pre>';
  1640. if ($negate && $this->exp->evaluate ($expr, $node, 'path', true)) {
  1641. if ($node['type'] == 'complete') {
  1642. return '';
  1643. }
  1644. $this->ignoreUntilLevel ($node['level']);
  1645. return '';
  1646. } elseif (! $negate && ! $this->exp->evaluate ($expr, $node, 'path', true)) {
  1647. if ($node['type'] == 'complete') {
  1648. return '';
  1649. }
  1650. $this->ignoreUntilLevel ($node['level']);
  1651. return '';
  1652. } else {
  1653. unset ($node['attributes'][$this->prefix . 'condition']);
  1654. }
  1655. }
  1656. }
  1657. }
  1658. foreach ($node['attributes'] as $key => $value) {
  1659. if (strstr ($value, '${')) {
  1660. $value = $this->exp->evaluate ('string: ' . $value, $node, 'path', true);
  1661. }
  1662. $out .= ' ' . $key . '="' . $value . '"';
  1663. }
  1664. }
  1665. if ($node['type'] == 'complete') {
  1666. if (isset ($node['value']) || ($this->isHtml && ! in_array ($node['tag'], $this->selfClosing))) {
  1667. $out .= '>' . $node['value'] . '</' . $node['tag'] . '>';
  1668. } else {
  1669. $out .= ' />';
  1670. }
  1671. } else {
  1672. $out .= '>';
  1673. if (isset ($node['value'])) {
  1674. $out .= $node['value'];
  1675. }
  1676. }
  1677. return $out;
  1678. }
  1679. /**
  1680. * Default close tag handler.
  1681. *
  1682. * @access private
  1683. * @param associative array $node
  1684. * @return string
  1685. *
  1686. */
  1687. function _default_end ($node) {
  1688. $out = '';
  1689. $iul = $this->ignoreUntilLevel ();
  1690. if ($iul && $iul < $node['level']) {
  1691. return '';
  1692. } elseif ($this->open && $node['level'] == $this->openUntilLevel && $this->isLoop) {
  1693. // xt:loop ends here
  1694. $this->open_var .= '</' . $node['tag'] . '>';
  1695. $loop = array_shift ($this->loop);
  1696. $this->open = false;
  1697. unset ($this->open_var);
  1698. $this->openUntilLevel = false;
  1699. $this->isLoop = false;
  1700. $list =& $this->exp->repeat ($loop['expr'], $node);
  1701. $total = count ($list);
  1702. foreach ($list as $key => $item) {
  1703. $this->exp->setCurrent ($item, $loop['varname'], $key, $total);
  1704. $out .= $this->fill ($this->wrap ($loop['struct']), $this->exp->getObject ('object'), true);
  1705. }
  1706. //$this->exp->setCurrent (new StdClass, $loop['varname'], 0, 0);
  1707. return $out;
  1708. } elseif ($iul && $node['level'] == $iul) {
  1709. $this->ignoreUntilLevel (-1);
  1710. return '';
  1711. }
  1712. return '</' . $node['tag'] . '>';
  1713. }
  1714. /**
  1715. * Default cdata node handler.
  1716. *
  1717. * @access private
  1718. * @param associative array $node
  1719. * @return string
  1720. *
  1721. */
  1722. function _default_cdata ($node) {
  1723. $iul = $this->ignoreUntilLevel ();
  1724. if ($iul && $iul < $node['level']) {
  1725. return '';
  1726. }
  1727. if ($this->_addToHeader) {
  1728. $this->toc[count ($this->toc) - 1]['value'] .= $node['value'];
  1729. }
  1730. return $node['value'];
  1731. }
  1732. /**
  1733. * Handler for xmlchar tags. See
  1734. * http://xmlchar.sf.net/ for more information.
  1735. *
  1736. * @access private
  1737. * @param associative array $node
  1738. * @return string
  1739. *
  1740. */
  1741. function _ch_handler ($node) {
  1742. $iul = $this->ignoreUntilLevel ();
  1743. if ($iul && $iul < $node['level']) {
  1744. return '';
  1745. }
  1746. if (preg_match ('/^ch:n[0-9]+$/', $node['tag'])) {
  1747. $node['tag'] = str_replace ('n', '#', $node['tag']);
  1748. }
  1749. return '&' . str_replace ('ch:', '', $node['tag']) . ';';
  1750. }
  1751. /**
  1752. * Handler for header tags, if $makeToc is true.
  1753. *
  1754. * @access private
  1755. * @param associative array $node
  1756. * @return string
  1757. *
  1758. */
  1759. function _header_handler ($node) {
  1760. $iul = $this->ignoreUntilLevel ();
  1761. if ($iul && $iul < $node['level']) {
  1762. return '';
  1763. }
  1764. $node['href'] = md5 ($node['tag'] . count ($this->toc) . $node['value']);
  1765. $this->toc[] = $node;
  1766. if ($node['type'] == 'complete') {
  1767. $end = $node['value'] . '</' . $node['tag'] . '>';
  1768. } else {
  1769. $this->_addToHeader = true;
  1770. $end = $node['value'];
  1771. }
  1772. $attrs = '';
  1773. if (is_array ($node['attributes'])) {
  1774. foreach ($node['attributes'] as $key => $value) {
  1775. if (strstr ($value, '${')) {
  1776. $value = $this->exp->evaluate ('string: ' . $value, $node, 'path', true);
  1777. }
  1778. $attrs .= ' ' . $key . '="' . $value . '"';
  1779. }
  1780. }
  1781. return '<a name="' . $node['href'] . '" style="margin: 0px; padding: 0px; display: inline"></a><' . $node['tag'] . $attrs . '>' . $end;
  1782. }
  1783. /**
  1784. * Handler for end header tags, if $makeToc is true.
  1785. *
  1786. * @access private
  1787. * @param associative array $node
  1788. * @return string
  1789. *
  1790. */
  1791. function _header_end ($node) {
  1792. $iul = $this->ignoreUntilLevel ();
  1793. if ($iul && $iul < $node['level']) {
  1794. return '';
  1795. }
  1796. $this->_addToHeader = false;
  1797. return '</' . $node['tag'] . '>';
  1798. }
  1799. /**
  1800. * Generates a table of contents as an HTML unordered
  1801. * list, based on the $toc property. Note: Also requires
  1802. * $buildToc to be set to true.
  1803. *
  1804. * @access public
  1805. * @param string $title
  1806. * @return string
  1807. *
  1808. */
  1809. function makeToc ($title = '') {
  1810. if (count ($this->toc) == 0 || ! $this->buildToc) {
  1811. return '';
  1812. }
  1813. $out = '';
  1814. if (! empty ($title)) {
  1815. $out .= '<h2>' . $title . '</h2>' . NEWLINEx2;
  1816. }
  1817. $prev = '';
  1818. $out .= '<ul>' . NEWLINE;
  1819. $c = 0;
  1820. foreach ($this->toc as $node) {
  1821. if ($prev == $node['tag'] || empty ($prev)) {
  1822. // same level
  1823. $out .= TAB . '<li><a href="#' . $node['href'] . '">' . $node['value'] . '</a></li>' . NEWLINE;
  1824. } elseif ($prev < $node['tag']) {
  1825. // this tag under
  1826. $c++;
  1827. $out .= '<ul>' . NEWLINE;
  1828. $out .= TAB . '<li><a href="#' . $node['href'] . '">' . $node['value'] . '</a></li>' . NEWLINE;
  1829. } elseif ($prev > $node['tag']) {
  1830. // close list and move down one
  1831. $c--;
  1832. $out .= '</ul>' . NEWLINE;
  1833. $out .= TAB . '<li><a href="#' . $node['href'] . '">' . $node['value'] . '</a></li>' . NEWLINE;
  1834. }
  1835. $prev = $node['tag'];
  1836. }
  1837. while ($c > 0) {
  1838. $out .= '</ul>' . NEWLINE;
  1839. $c--;
  1840. }
  1841. return $out . '</ul>' . NEWLINEx2;
  1842. }
  1843. // <xt:tpl>
  1844. /**
  1845. * Open tpl tag handler.
  1846. *
  1847. * @access private
  1848. * @param associative array $node
  1849. * @return string
  1850. *
  1851. */
  1852. function _tpl ($node) {
  1853. return $node['value'];
  1854. }
  1855. // </xt:tpl>
  1856. /**
  1857. * Close tpl tag handler.
  1858. *
  1859. * @access private
  1860. * @param associative array $node
  1861. * @return string
  1862. *
  1863. */
  1864. function _tpl_end ($node) {
  1865. if (! isset ($node['value'])) {
  1866. return '';
  1867. }
  1868. return $node['value'];
  1869. }
  1870. /**
  1871. * Cdata tpl tag handler.
  1872. *
  1873. * @access private
  1874. * @param associative array $node
  1875. * @return string
  1876. *
  1877. */
  1878. function _tpl_cdata ($node) {
  1879. return $node['value'];
  1880. }
  1881. // <xt:doctype root="html" access="public" name="" uri="" />
  1882. /**
  1883. * Creates a doctype declaration from an xt:doctype
  1884. * tag.
  1885. *
  1886. * @access private
  1887. * @param associative array $node
  1888. * @return string
  1889. *
  1890. */
  1891. function _doctype ($node) {
  1892. $out = '<!DOCTYPE ' . $node['attributes']['root'];
  1893. $out .= ' ' . strtoupper ($node['attributes']['access']) . ' "';
  1894. if (isset ($node['attributes']['name'])) {
  1895. $out .= $node['attributes']['name'] . '" "';
  1896. }
  1897. $out .= $node['attributes']['uri'] . "\">\n";
  1898. return $out;
  1899. }
  1900. // <xt:xmldecl version="1.0" encoding="utf-8" />
  1901. /**
  1902. * Creates an xml declaration tag from an xt:xmldecl
  1903. * tag.
  1904. *
  1905. * @access private
  1906. * @param associative array $node
  1907. * @return string
  1908. *
  1909. */
  1910. function _xmldecl ($node) {
  1911. $out = '<?xml';
  1912. foreach ($node['attributes'] as $key => $value) {
  1913. $out .= ' ' . $key . '="' . $value . '"';
  1914. }
  1915. $out .= CLOSE_TAG;
  1916. return $out;
  1917. }
  1918. // <xt:xmlst…

Large files files are truncated, but you can click here to view the full file