PageRenderTime 54ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/treeview-master/libs/Nette/Web/Html.php

https://github.com/indesigner/tests
PHP | 609 lines | 271 code | 138 blank | 200 comment | 47 complexity | aaf5519100648df87b5a7c7b26e1c001 MD5 | raw file
Possible License(s): BSD-3-Clause, GPL-2.0
  1. <?php
  2. /**
  3. * Nette Framework
  4. *
  5. * @copyright Copyright (c) 2004, 2010 David Grudl
  6. * @license http://nettephp.com/license Nette license
  7. * @link http://nettephp.com
  8. * @category Nette
  9. * @package Nette\Web
  10. */
  11. /**
  12. * HTML helper.
  13. *
  14. * <code>
  15. * $anchor = Html::el('a')->href($link)->setText('Nette');
  16. * $el->class = 'myclass';
  17. * echo $el;
  18. *
  19. * echo $el->startTag(), $el->endTag();
  20. * </code>
  21. *
  22. * @copyright Copyright (c) 2004, 2010 David Grudl
  23. * @package Nette\Web
  24. */
  25. class Html extends Object implements ArrayAccess, Countable, IteratorAggregate
  26. {
  27. /** @var string element's name */
  28. private $name;
  29. /** @var bool is element empty? */
  30. private $isEmpty;
  31. /** @var array element's attributes */
  32. public $attrs = array();
  33. /** @var array of Html | string nodes */
  34. protected $children = array();
  35. /** @var bool use XHTML syntax? */
  36. public static $xhtml = TRUE;
  37. /** @var array empty elements */
  38. public static $emptyElements = array('img'=>1,'hr'=>1,'br'=>1,'input'=>1,'meta'=>1,'area'=>1,'command'=>1,'keygen'=>1,'source'=>1,
  39. 'base'=>1,'col'=>1,'link'=>1,'param'=>1,'basefont'=>1,'frame'=>1,'isindex'=>1,'wbr'=>1,'embed'=>1);
  40. /**
  41. * Static factory.
  42. * @param string element name (or NULL)
  43. * @param array|string element's attributes (or textual content)
  44. * @return Html
  45. */
  46. public static function el($name = NULL, $attrs = NULL)
  47. {
  48. $el = new self ;
  49. $parts = explode(' ', $name, 2);
  50. $el->setName($parts[0]);
  51. if (is_array($attrs)) {
  52. $el->attrs = $attrs;
  53. } elseif ($attrs !== NULL) {
  54. $el->setText($attrs);
  55. }
  56. if (isset($parts[1])) {
  57. preg_match_all('#([a-z0-9:-]+)(?:=(["\'])?(.*?)(?(2)\\2|\s))?#i', $parts[1] . ' ', $parts, PREG_SET_ORDER);
  58. foreach ($parts as $m) {
  59. $el->attrs[$m[1]] = isset($m[3]) ? $m[3] : TRUE;
  60. }
  61. }
  62. return $el;
  63. }
  64. /**
  65. * Changes element's name.
  66. * @param string
  67. * @param bool Is element empty?
  68. * @return Html provides a fluent interface
  69. * @throws InvalidArgumentException
  70. */
  71. final public function setName($name, $isEmpty = NULL)
  72. {
  73. if ($name !== NULL && !is_string($name)) {
  74. throw new InvalidArgumentException("Name must be string or NULL, " . gettype($name) ." given.");
  75. }
  76. $this->name = $name;
  77. $this->isEmpty = $isEmpty === NULL ? isset(self::$emptyElements[$name]) : (bool) $isEmpty;
  78. return $this;
  79. }
  80. /**
  81. * Returns element's name.
  82. * @return string
  83. */
  84. final public function getName()
  85. {
  86. return $this->name;
  87. }
  88. /**
  89. * Is element empty?
  90. * @return bool
  91. */
  92. final public function isEmpty()
  93. {
  94. return $this->isEmpty;
  95. }
  96. /**
  97. * Overloaded setter for element's attribute.
  98. * @param string HTML attribute name
  99. * @param mixed HTML attribute value
  100. * @return void
  101. */
  102. final public function __set($name, $value)
  103. {
  104. $this->attrs[$name] = $value;
  105. }
  106. /**
  107. * Overloaded getter for element's attribute.
  108. * @param string HTML attribute name
  109. * @return mixed HTML attribute value
  110. */
  111. final public function &__get($name)
  112. {
  113. return $this->attrs[$name];
  114. }
  115. /**
  116. * Overloaded unsetter for element's attribute.
  117. * @param string HTML attribute name
  118. * @return void
  119. */
  120. final public function __unset($name)
  121. {
  122. unset($this->attrs[$name]);
  123. }
  124. /**
  125. * Overloaded setter for element's attribute.
  126. * @param string HTML attribute name
  127. * @param array (string) HTML attribute value or pair?
  128. * @return Html provides a fluent interface
  129. */
  130. final public function __call($m, $args)
  131. {
  132. $p = substr($m, 0, 3);
  133. if ($p === 'get' || $p === 'set' || $p === 'add') {
  134. $m = substr($m, 3);
  135. $m[0] = $m[0] | "\x20";
  136. if ($p === 'get') {
  137. return isset($this->attrs[$m]) ? $this->attrs[$m] : NULL;
  138. } elseif ($p === 'add') {
  139. $args[] = TRUE;
  140. }
  141. }
  142. if (count($args) === 1) { // set
  143. $this->attrs[$m] = $args[0];
  144. } elseif ($args[0] == NULL) { // intentionally ==
  145. $tmp = & $this->attrs[$m]; // appending empty value? -> ignore, but ensure it exists
  146. } elseif (!isset($this->attrs[$m]) || is_array($this->attrs[$m])) { // needs array
  147. $this->attrs[$m][$args[0]] = $args[1];
  148. } else {
  149. $this->attrs[$m] = array($this->attrs[$m], $args[0] => $args[1]);
  150. }
  151. return $this;
  152. }
  153. /**
  154. * Special setter for element's attribute.
  155. * @param string path
  156. * @param array query
  157. * @return Html provides a fluent interface
  158. */
  159. final public function href($path, $query = NULL)
  160. {
  161. if ($query) {
  162. $query = http_build_query($query, NULL, '&');
  163. if ($query !== '') $path .= '?' . $query;
  164. }
  165. $this->attrs['href'] = $path;
  166. return $this;
  167. }
  168. /**
  169. * Sets element's HTML content.
  170. * @param string
  171. * @return Html provides a fluent interface
  172. * @throws InvalidArgumentException
  173. */
  174. final public function setHtml($html)
  175. {
  176. if ($html === NULL) {
  177. $html = '';
  178. } elseif (is_array($html)) {
  179. throw new InvalidArgumentException("Textual content must be a scalar, " . gettype($html) ." given.");
  180. } else {
  181. $html = (string) $html;
  182. }
  183. $this->removeChildren();
  184. $this->children[] = $html;
  185. return $this;
  186. }
  187. /**
  188. * Gets element's textual content.
  189. * @return string
  190. */
  191. final public function getHtml()
  192. {
  193. $s = '';
  194. foreach ($this->children as $child) {
  195. if (is_object($child)) return FALSE;
  196. $s .= $child;
  197. }
  198. return $s;
  199. }
  200. /**
  201. * Sets element's textual content.
  202. * @param string
  203. * @return Html provides a fluent interface
  204. * @throws InvalidArgumentException
  205. */
  206. final public function setText($text)
  207. {
  208. if (!is_array($text)) {
  209. $text = str_replace(array('&', '<', '>'), array('&amp;', '&lt;', '&gt;'), (string) $text);
  210. }
  211. return $this->setHtml($text);
  212. }
  213. /**
  214. * @deprecated
  215. */
  216. final public function getText()
  217. {
  218. return $this->getHtml();
  219. }
  220. /**
  221. * Adds new element's child.
  222. * @param Html|string child node
  223. * @return Html provides a fluent interface
  224. */
  225. final public function add($child)
  226. {
  227. return $this->insert(NULL, $child);
  228. }
  229. /**
  230. * Creates and adds a new Html child.
  231. * @param string elements's name
  232. * @param array|string element's attributes (or textual content)
  233. * @return Html created element
  234. */
  235. final public function create($name, $attrs = NULL)
  236. {
  237. $this->insert(NULL, $child = self ::el($name, $attrs));
  238. return $child;
  239. }
  240. /**
  241. * Inserts child node.
  242. * @param int
  243. * @param Html node
  244. * @param bool
  245. * @return Html provides a fluent interface
  246. * @throws Exception
  247. */
  248. public function insert($index, $child, $replace = FALSE)
  249. {
  250. if ($child instanceof Html || is_scalar($child)) {
  251. if ($index === NULL) { // append
  252. $this->children[] = $child;
  253. } else { // insert or replace
  254. array_splice($this->children, (int) $index, $replace ? 1 : 0, array($child));
  255. }
  256. } else {
  257. throw new InvalidArgumentException("Child node must be scalar or Html object, " . (is_object($child) ? get_class($child) : gettype($child)) ." given.");
  258. }
  259. return $this;
  260. }
  261. /**
  262. * Inserts (replaces) child node (\ArrayAccess implementation).
  263. * @param int
  264. * @param Html node
  265. * @return void
  266. */
  267. final public function offsetSet($index, $child)
  268. {
  269. $this->insert($index, $child, TRUE);
  270. }
  271. /**
  272. * Returns child node (\ArrayAccess implementation).
  273. * @param int index
  274. * @return mixed
  275. */
  276. final public function offsetGet($index)
  277. {
  278. return $this->children[$index];
  279. }
  280. /**
  281. * Exists child node? (\ArrayAccess implementation).
  282. * @param int index
  283. * @return bool
  284. */
  285. final public function offsetExists($index)
  286. {
  287. return isset($this->children[$index]);
  288. }
  289. /**
  290. * Removes child node (\ArrayAccess implementation).
  291. * @param int index
  292. * @return void
  293. */
  294. public function offsetUnset($index)
  295. {
  296. if (isset($this->children[$index])) {
  297. array_splice($this->children, (int) $index, 1);
  298. }
  299. }
  300. /**
  301. * Required by the \Countable interface.
  302. * @return int
  303. */
  304. final public function count()
  305. {
  306. return count($this->children);
  307. }
  308. /**
  309. * Removed all children.
  310. * @return void
  311. */
  312. public function removeChildren()
  313. {
  314. $this->children = array();
  315. }
  316. /**
  317. * Iterates over a elements.
  318. * @param bool recursive?
  319. * @param string class types filter
  320. * @return RecursiveIterator
  321. */
  322. final public function getIterator($deep = FALSE)
  323. {
  324. if ($deep) {
  325. $deep = $deep > 0 ? RecursiveIteratorIterator::SELF_FIRST : RecursiveIteratorIterator::CHILD_FIRST;
  326. return new RecursiveIteratorIterator(new RecursiveHtmlIterator($this->children), $deep);
  327. } else {
  328. return new RecursiveHtmlIterator($this->children);
  329. }
  330. }
  331. /**
  332. * Returns all of children.
  333. * return array
  334. */
  335. final public function getChildren()
  336. {
  337. return $this->children;
  338. }
  339. /**
  340. * Renders element's start tag, content and end tag.
  341. * @param int indent
  342. * @return string
  343. */
  344. final public function render($indent = NULL)
  345. {
  346. $s = $this->startTag();
  347. // empty elements are finished now
  348. if ($this->isEmpty) {
  349. return $s;
  350. }
  351. // add content
  352. if ($indent !== NULL) {
  353. $indent++;
  354. }
  355. foreach ($this->children as $child) {
  356. if (is_object($child)) {
  357. $s .= $child->render($indent);
  358. } else {
  359. $s .= $child;
  360. }
  361. }
  362. // add end tag
  363. $s .= $this->endTag();
  364. if ($indent !== NULL) {
  365. return "\n" . str_repeat("\t", $indent - 1) . $s . "\n" . str_repeat("\t", max(0, $indent - 2));
  366. }
  367. return $s;
  368. }
  369. final public function __toString()
  370. {
  371. return $this->render();
  372. }
  373. /**
  374. * Returns element's start tag.
  375. * @return string
  376. */
  377. final public function startTag()
  378. {
  379. if ($this->name) {
  380. return '<' . $this->name . $this->attributes() . (self::$xhtml && $this->isEmpty ? ' />' : '>');
  381. } else {
  382. return '';
  383. }
  384. }
  385. /**
  386. * Returns element's end tag.
  387. * @return string
  388. */
  389. final public function endTag()
  390. {
  391. return $this->name && !$this->isEmpty ? '</' . $this->name . '>' : '';
  392. }
  393. /**
  394. * Returns element's attributes.
  395. * @return string
  396. */
  397. final public function attributes()
  398. {
  399. if (!is_array($this->attrs)) {
  400. return '';
  401. }
  402. $s = '';
  403. foreach ($this->attrs as $key => $value)
  404. {
  405. // skip NULLs and false boolean attributes
  406. if ($value === NULL || $value === FALSE) continue;
  407. // true boolean attribute
  408. if ($value === TRUE) {
  409. // in XHTML must use unminimized form
  410. if (self::$xhtml) $s .= ' ' . $key . '="' . $key . '"';
  411. // in HTML should use minimized form
  412. else $s .= ' ' . $key;
  413. continue;
  414. } elseif (is_array($value)) {
  415. // prepare into temporary array
  416. $tmp = NULL;
  417. foreach ($value as $k => $v) {
  418. // skip NULLs & empty string; composite 'style' vs. 'others'
  419. if ($v == NULL) continue; // intentionally ==
  420. // composite 'style' vs. 'others'
  421. $tmp[] = is_string($k) ? ($v === TRUE ? $k : $k . ':' . $v) : $v;
  422. }
  423. if ($tmp === NULL) continue;
  424. $value = implode($key === 'style' || !strncmp($key, 'on', 2) ? ';' : ' ', $tmp);
  425. } else {
  426. $value = (string) $value;
  427. }
  428. // add new attribute
  429. $s .= ' ' . $key . '="'
  430. . str_replace(array('&', '"', '<', '>', '@'), array('&amp;', '&quot;', '&lt;', '&gt;', '&#64;'), $value)
  431. . '"';
  432. }
  433. return $s;
  434. }
  435. /**
  436. * Clones all children too.
  437. */
  438. public function __clone()
  439. {
  440. foreach ($this->children as $key => $value) {
  441. if (is_object($value)) {
  442. $this->children[$key] = clone $value;
  443. }
  444. }
  445. }
  446. }
  447. /**
  448. * Recursive HTML element iterator. See Html::getIterator().
  449. *
  450. * @copyright Copyright (c) 2004, 2010 David Grudl
  451. * @package Nette\Web
  452. */
  453. class RecursiveHtmlIterator extends RecursiveArrayIterator implements Countable
  454. {
  455. /**
  456. * The sub-iterator for the current element.
  457. * @return RecursiveIterator
  458. */
  459. public function getChildren()
  460. {
  461. return $this->current()->getIterator();
  462. }
  463. /**
  464. * Returns the count of elements.
  465. * @return int
  466. */
  467. public function count()
  468. {
  469. return iterator_count($this);
  470. }
  471. }