PageRenderTime 54ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/saf/lib/XML/XT.php

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

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

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