QPList.php 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. <?php
  2. /** @file
  3. * This extension provides support for common HTML list operations.
  4. */
  5. /**
  6. * Provide list operations for QueryPath.
  7. *
  8. * The QPList class is an extension to QueryPath. It provides HTML list generators
  9. * that take lists and convert them into bulleted lists inside of QueryPath.
  10. *
  11. * @deprecated This will be removed from a subsequent version of QueryPath. It will
  12. * be released as a stand-alone extension.
  13. * @ingroup querypath_extensions
  14. */
  15. class QPList implements QueryPathExtension {
  16. const UL = 'ul';
  17. const OL = 'ol';
  18. const DL = 'dl';
  19. protected $qp = NULL;
  20. public function __construct(QueryPath $qp) {
  21. $this->qp = $qp;
  22. }
  23. public function appendTable($items, $options = array()) {
  24. $opts = $options + array(
  25. 'table class' => 'qptable',
  26. );
  27. $base = '<?xml version="1.0"?>
  28. <table>
  29. <tbody>
  30. <tr></tr>
  31. </tbody>
  32. </table>';
  33. $qp = qp($base, 'table')->addClass($opts['table class'])->find('tr');
  34. if ($items instanceof TableAble) {
  35. $headers = $items->getHeaders();
  36. $rows = $items->getRows();
  37. }
  38. elseif ($items instanceof Traversable) {
  39. $headers = array();
  40. $rows = $items;
  41. }
  42. else {
  43. $headers = $items['headers'];
  44. $rows = $items['rows'];
  45. }
  46. // Add Headers:
  47. foreach ($headers as $header) {
  48. $qp->append('<th>' . $header . '</th>');
  49. }
  50. $qp->top()->find('tr:last');
  51. // Add rows and cells.
  52. foreach ($rows as $row) {
  53. $qp->after('<tr/>')->next();
  54. foreach($row as $cell) $qp->append('<td>' . $cell . '</td>');
  55. }
  56. $this->qp->append($qp->top());
  57. return $this->qp;
  58. }
  59. /**
  60. * Append a list of items into an HTML DOM using one of the HTML list structures.
  61. * This takes a one-dimensional array and converts it into an HTML UL or OL list,
  62. * <b>or</b> it can take an associative array and convert that into a DL list.
  63. *
  64. * In addition to arrays, this works with any Traversable or Iterator object.
  65. *
  66. * OL/UL arrays can be nested.
  67. *
  68. * @param mixed $items
  69. * An indexed array for UL and OL, or an associative array for DL. Iterator and
  70. * Traversable objects can also be used.
  71. * @param string $type
  72. * One of ul, ol, or dl. Predefined constants are available for use.
  73. * @param array $options
  74. * An associative array of configuration options. The supported options are:
  75. * - 'list class': The class that will be assigned to a list.
  76. */
  77. public function appendList($items, $type = self::UL, $options = array()) {
  78. $opts = $options + array(
  79. 'list class' => 'qplist',
  80. );
  81. if ($type == self::DL) {
  82. $q = qp('<?xml version="1.0"?><dl></dl>', 'dl')->addClass($opts['list class']);
  83. foreach ($items as $dt => $dd) {
  84. $q->append('<dt>' . $dt . '</dt><dd>' . $dd . '</dd>');
  85. }
  86. $q->appendTo($this->qp);
  87. }
  88. else {
  89. $q = $this->listImpl($items, $type, $opts);
  90. $this->qp->append($q->find(':root'));
  91. }
  92. return $this->qp;
  93. }
  94. /**
  95. * Internal recursive list generator for appendList.
  96. */
  97. protected function listImpl($items, $type, $opts, $q = NULL) {
  98. $ele = '<' . $type . '/>';
  99. if (!isset($q))
  100. $q = qp()->append($ele)->addClass($opts['list class']);
  101. foreach ($items as $li) {
  102. if ($li instanceof QueryPath) {
  103. $q = $this->listImpl($li->get(), $type, $opts, $q);
  104. }
  105. elseif (is_array($li) || $li instanceof Traversable) {
  106. $q->append('<li><ul/></li>')->find('li:last > ul');
  107. $q = $this->listImpl($li, $type, $opts, $q);
  108. $q->parent();
  109. }
  110. else {
  111. $q->append('<li>' . $li . '</li>');
  112. }
  113. }
  114. return $q;
  115. }
  116. /**
  117. * Unused.
  118. */
  119. protected function isAssoc($array) {
  120. // A clever method from comment on is_array() doc page:
  121. return count(array_diff_key($array, range(0, count($array) - 1))) != 0;
  122. }
  123. }
  124. QueryPathExtensionRegistry::extend('QPList');
  125. /**
  126. * A TableAble object represents tabular data and can be converted to a table.
  127. *
  128. * The {@link QPList} extension to {@link QueryPath} provides a method for
  129. * appending a table to a DOM ({@link QPList::appendTable()}).
  130. *
  131. * Implementing classes should provide methods for getting headers, rows
  132. * of data, and the number of rows in the table ({@link TableAble::size()}).
  133. * Implementors may also choose to make classes Iterable or Traversable over
  134. * the rows of the table.
  135. *
  136. * Two very basic implementations of TableAble are provided in this package:
  137. * - {@link QPTableData} provides a generic implementation.
  138. * - {@link QPTableTextData} provides a generic implementation that also escapes
  139. * all data.
  140. */
  141. interface TableAble {
  142. public function getHeaders();
  143. public function getRows();
  144. public function size();
  145. }
  146. /**
  147. * Format data to be inserted into a simple HTML table.
  148. *
  149. * Data in the headers or rows may contain markup. If you want to
  150. * disallow markup, use a {@see QPTableTextData} object instead.
  151. */
  152. class QPTableData implements TableAble, IteratorAggregate {
  153. protected $headers;
  154. protected $rows;
  155. protected $caption;
  156. protected $p = -1;
  157. public function setHeaders($array) {$this->headers = $array; return $this;}
  158. public function getHeaders() {return $this->headers; }
  159. public function setRows($array) {$this->rows = $array; return $this;}
  160. public function getRows() {return $this->rows;}
  161. public function size() {return count($this->rows);}
  162. public function getIterator() {
  163. return new ArrayIterator($rows);
  164. }
  165. }
  166. /**
  167. * Provides a table where all of the headers and data are treated as text data.
  168. *
  169. * This provents marked-up data from being inserted into the DOM as elements.
  170. * Instead, the text is escaped using {@see htmlentities()}.
  171. *
  172. * @see QPTableData
  173. */
  174. class QPTableTextData extends QPTableData {
  175. public function setHeaders($array) {
  176. $headers = array();
  177. foreach ($array as $header) {
  178. $headers[] = htmlentities($header);
  179. }
  180. parent::setHeaders($headers);
  181. return $this;
  182. }
  183. public function setRows($array) {
  184. $count = count($array);
  185. for ($i = 0; $i < $count; ++$i) {
  186. $cols = array();
  187. foreach ($data[$i] as $datum) {
  188. $cols[] = htmlentities($datum);
  189. }
  190. $data[$i] = $cols;
  191. }
  192. parent::setRows($array);
  193. return $this;
  194. }
  195. }