PageRenderTime 49ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/php-lib/html.php

http://github.com/facebook/xhp
PHP | 741 lines | 558 code | 103 blank | 80 comment | 5 complexity | 4ce840e64096b519ce84458342c9e4e5 MD5 | raw file
Possible License(s): MIT, MPL-2.0-no-copyleft-exception
  1. <?php
  2. /*
  3. +----------------------------------------------------------------------+
  4. | XHP |
  5. +----------------------------------------------------------------------+
  6. | Copyright (c) 2009 - 2010 Facebook, Inc. (http://www.facebook.com) |
  7. +----------------------------------------------------------------------+
  8. | This source file is subject to version 3.01 of the PHP license, |
  9. | that is bundled with this package in the file LICENSE.PHP, and is |
  10. | available through the world-wide-web at the following url: |
  11. | http://www.php.net/license/3_01.txt |
  12. | If you did not receive a copy of the PHP license and are unable to |
  13. | obtain it through the world-wide-web, please send a note to |
  14. | license@php.net so we can mail you a copy immediately. |
  15. +----------------------------------------------------------------------+
  16. */
  17. /**
  18. * This is the base library of HTML elements for use in XHP. This includes all
  19. * non-deprecated tags and attributes. Elements in this file should stay as
  20. * close to spec as possible. Facebook-specific extensions should go into their
  21. * own elements.
  22. */
  23. abstract class :xhp:html-element extends :x:primitive {
  24. // TODO: Break these out into abstract elements so that elements that need
  25. // them can steal the definition. Right now this is an overloaded list of
  26. // attributes.
  27. attribute
  28. // HTML attributes
  29. string accesskey, string class, string dir, string id, string lang,
  30. string style, string tabindex, string title,
  31. // Javascript events
  32. string onabort, string onblur, string onchange, string onclick,
  33. string ondblclick, string onerror, string onfocus, string onkeydown,
  34. string onkeypress, string onkeyup, string onload, string onmousedown,
  35. string onmousemove, string onmouseout, string onmouseover, string onmouseup,
  36. string onreset, string onresize, string onselect, string onsubmit,
  37. string onunload,
  38. // IE only
  39. string onmouseenter, string onmouseleave,
  40. // Joe Hewitt!!
  41. // TODO:
  42. string selected, string otherButtonLabel, string otherButtonHref,
  43. string otherButtonClass, string type, string replaceCaret,
  44. string replaceChildren;
  45. protected
  46. $tagName;
  47. public function requireUniqueId() {
  48. // TODO: Implement something on AsyncRequest that returns the number of
  49. // requests sent so far so we can remove the microtime(true) thing.
  50. if (!($id = $this->getAttribute('id'))) {
  51. $this->setAttribute('id', $id = substr(md5(mt_rand(0, 100000)), 0, 10));
  52. }
  53. return $id;
  54. }
  55. protected final function renderBaseAttrs() {
  56. $buf = '<'.$this->tagName;
  57. foreach ($this->getAttributes() as $key => $val) {
  58. if ($val !== null && $val !== false) {
  59. $buf .= ' ' . htmlspecialchars($key) . '="' . htmlspecialchars($val, true) . '"';
  60. }
  61. }
  62. return $buf;
  63. }
  64. public function addClass($class) {
  65. $class = trim($class);
  66. $currentClasses = $this->getAttribute('class');
  67. $has = strpos(' '.$currentClasses.' ', ' '.$class.' ') !== false;
  68. if ($has) {
  69. return $this;
  70. }
  71. $this->setAttribute('class', trim($currentClasses.' '.$class));
  72. return $this;
  73. }
  74. protected function stringify() {
  75. $buf = $this->renderBaseAttrs() . '>';
  76. foreach ($this->getChildren() as $child) {
  77. $buf .= :x:base::renderChild($child);
  78. }
  79. $buf .= '</'.$this->tagName.'>';
  80. return $buf;
  81. }
  82. }
  83. /**
  84. * Subclasses of :xhp:html-singleton may not contain children. When rendered they
  85. * will be in singleton (<img />, <br />) form.
  86. */
  87. abstract class :xhp:html-singleton extends :xhp:html-element {
  88. children empty;
  89. protected function stringify() {
  90. return $this->renderBaseAttrs() . ' />';
  91. }
  92. }
  93. /**
  94. * Subclasses of :xhp:pseudo-singleton may contain exactly zero or one
  95. * children. When rendered they will be in full open\close form, no matter how
  96. * many children there are.
  97. */
  98. abstract class :xhp:pseudo-singleton extends :xhp:html-element {
  99. children (pcdata)*;
  100. protected function escape($txt) {
  101. return htmlspecialchars($txt);
  102. }
  103. protected function stringify() {
  104. $buf = $this->renderBaseAttrs() . '>';
  105. if ($children = $this->getChildren()) {
  106. $buf .= :x:base::renderChild($children[0]);
  107. }
  108. return $buf . '</'.$this->tagName.'>';
  109. }
  110. }
  111. /**
  112. * Below is a big wall of element definitions. These are the basic building
  113. * blocks of XHP pages.
  114. */
  115. class :a extends :xhp:html-element {
  116. attribute
  117. string href, string name, string rel, string target;
  118. category %flow, %phrase, %interactive;
  119. // transparent
  120. // may not contain %interactive
  121. children (pcdata | %flow)*;
  122. protected $tagName = 'a';
  123. }
  124. class :abbr extends :xhp:html-element {
  125. category %flow, %phrase;
  126. children (pcdata | %phrase)*;
  127. protected $tagName = 'abbr';
  128. }
  129. class :acronym extends :xhp:html-element {
  130. category %flow, %phrase;
  131. children (pcdata | %phrase)*;
  132. protected $tagName = 'acronym';
  133. }
  134. class :address extends :xhp:html-element {
  135. category %flow;
  136. // may not contain h1-h6
  137. children (pcdata | %flow)*;
  138. protected $tagName = 'address';
  139. }
  140. class :area extends :xhp:html-singleton {
  141. attribute string alt, string coords, string href, bool nohref, string target;
  142. protected $tagName = 'area';
  143. }
  144. class :b extends :xhp:html-element {
  145. category %flow, %phrase;
  146. children (pcdata | %phrase)*;
  147. protected $tagName = 'b';
  148. }
  149. class :base extends :xhp:html-singleton {
  150. attribute string href, string target;
  151. // also a member of "metadata", but is not listed here. see comments in :head
  152. // for more information
  153. protected $tagName = 'base';
  154. }
  155. class :big extends :xhp:html-element {
  156. category %flow, %phrase;
  157. children (pcdata | %phrase)*;
  158. protected $tagName = 'big';
  159. }
  160. class :blockquote extends :xhp:html-element {
  161. attribute string cite;
  162. category %flow;
  163. children (pcdata | %flow)*;
  164. protected $tagName = 'blockquote';
  165. }
  166. class :body extends :xhp:html-element {
  167. children (pcdata | %flow)*;
  168. protected $tagName = 'body';
  169. }
  170. class :br extends :xhp:html-singleton {
  171. category %flow, %phrase;
  172. protected $tagName = 'br';
  173. }
  174. class :button extends :xhp:html-element {
  175. attribute
  176. bool disabled, string name, enum { "submit", "button", "reset" } type, string value;
  177. category %flow, %phrase, %interactive;
  178. // may not contain interactive
  179. children (pcdata | %phrase)*;
  180. protected $tagName = 'button';
  181. }
  182. class :caption extends :xhp:html-element {
  183. // may not contain table
  184. children (pcdata | %flow)*;
  185. protected $tagName = 'caption';
  186. }
  187. class :cite extends :xhp:html-element {
  188. category %flow, %phrase;
  189. children (pcdata | %phrase)*;
  190. protected $tagName = 'cite';
  191. }
  192. class :code extends :xhp:html-element {
  193. category %flow, %phrase;
  194. children (pcdata | %phrase)*;
  195. protected $tagName = 'code';
  196. }
  197. class :col extends :xhp:html-singleton {
  198. attribute
  199. enum { "left", "right", "center", "justify", "char" } align, string char,
  200. int charoff, int span,
  201. enum { "top", "middle", "bottom", "baseline" } valign, string width;
  202. protected $tagName = 'col';
  203. }
  204. class :colgroup extends :xhp:html-element {
  205. attribute
  206. enum { "left", "right", "center", "justify", "char" } align, string char,
  207. int charoff, int span,
  208. enum { "top", "middle", "bottom", "baseline" } valign, string width;
  209. children (:col)*;
  210. protected $tagName = 'colgroup';
  211. }
  212. class :dd extends :xhp:html-element {
  213. children (pcdata | %flow)*;
  214. protected $tagName = 'dd';
  215. }
  216. class :del extends :xhp:html-element {
  217. attribute string cite, string datetime;
  218. category %flow, %phrase;
  219. // transparent
  220. children (pcdata | %flow)*;
  221. protected $tagName = 'del';
  222. }
  223. class :div extends :xhp:html-element {
  224. category %flow;
  225. children (pcdata | %flow)*;
  226. protected $tagName = 'div';
  227. }
  228. class :dfn extends :xhp:html-element {
  229. category %flow, %phrase;
  230. children (pcdata | %phrase)*;
  231. protected $tagName = 'dfn';
  232. }
  233. class :dl extends :xhp:html-element {
  234. category %flow;
  235. children (:dt+, :dd+)*;
  236. protected $tagName = 'dl';
  237. }
  238. class :dt extends :xhp:html-element {
  239. children (pcdata | %phrase)*;
  240. protected $tagName = 'dt';
  241. }
  242. class :em extends :xhp:html-element {
  243. category %flow, %phrase;
  244. children (pcdata | %phrase)*;
  245. protected $tagName = 'em';
  246. }
  247. class :fieldset extends :xhp:html-element {
  248. category %flow;
  249. children (:legend?, (pcdata | %flow)*);
  250. protected $tagName = 'fieldset';
  251. }
  252. class :form extends :xhp:html-element {
  253. attribute
  254. string action, string accept, string accept-charset, string enctype,
  255. enum { "get", "post" } method, string name, string target, bool ajaxify;
  256. category %flow;
  257. // may not contain form
  258. children (pcdata | %flow)*;
  259. protected $tagName = 'form';
  260. }
  261. class :frame extends :xhp:html-singleton {
  262. attribute
  263. bool frameborder, string longdesc, int marginheight, int marginwidth,
  264. string name, bool noresize, enum { "yes", "no", "auto" } scrolling,
  265. string src;
  266. protected $tagName = 'frame';
  267. }
  268. class :frameset extends :xhp:html-element {
  269. children (:frame | :frameset | :noframes)*;
  270. protected $tagName = 'frameset';
  271. }
  272. class :h1 extends :xhp:html-element {
  273. category %flow;
  274. children (pcdata | %phrase)*;
  275. protected $tagName = 'h1';
  276. }
  277. class :h2 extends :xhp:html-element {
  278. category %flow;
  279. children (pcdata | %phrase)*;
  280. protected $tagName = 'h2';
  281. }
  282. class :h3 extends :xhp:html-element {
  283. category %flow;
  284. children (pcdata | %phrase)*;
  285. protected $tagName = 'h3';
  286. }
  287. class :h4 extends :xhp:html-element {
  288. category %flow;
  289. children (pcdata | %phrase)*;
  290. protected $tagName = 'h4';
  291. }
  292. class :h5 extends :xhp:html-element {
  293. category %flow;
  294. children (pcdata | %phrase)*;
  295. protected $tagName = 'h5';
  296. }
  297. class :h6 extends :xhp:html-element {
  298. category %flow;
  299. children (pcdata | %phrase)*;
  300. protected $tagName = 'h6';
  301. }
  302. class :head extends :xhp:html-element {
  303. attribute string profile;
  304. children (%metadata*, :title, %metadata*, :base?, %metadata*);
  305. // Note: html/xhtml spec says that there should be exactly 1 <title />, and at
  306. // most 1 <base />. These elements can occur in any order, and can be
  307. // surrounded by any number of other elements (in %metadata). The problem
  308. // here is that XHP's validation does not backtrack, so there's no way to
  309. // accurately implement the spec. This is the closest we can get. The only
  310. // difference between this and the spec is that in XHP the <title /> must
  311. // appear before the <base />.
  312. protected $tagName = 'head';
  313. }
  314. class :hr extends :xhp:html-singleton {
  315. category %flow;
  316. protected $tagName = 'hr';
  317. }
  318. class :html extends :xhp:html-element {
  319. attribute string xmlns;
  320. children (:head, :body);
  321. protected $tagName = 'html';
  322. }
  323. class :i extends :xhp:html-element {
  324. category %flow, %phrase;
  325. children (pcdata | %phrase)*;
  326. protected $tagName = 'i';
  327. }
  328. class :iframe extends :xhp:pseudo-singleton {
  329. attribute
  330. enum {"1", "0"} frameborder,
  331. string height, string longdesc, int marginheight,
  332. int marginwidth, string name, enum { "yes", "no", "auto" } scrolling,
  333. string src, string width;
  334. category %flow, %phrase, %interactive;
  335. children empty;
  336. protected $tagName = 'iframe';
  337. }
  338. class :img extends :xhp:html-singleton {
  339. attribute
  340. // Lite
  341. string staticsrc,
  342. // HTML
  343. string alt, string src, string height, bool ismap, string longdesc,
  344. string usemap, string width;
  345. category %flow, %phrase;
  346. protected $tagName = 'img';
  347. }
  348. class :input extends :xhp:html-singleton {
  349. attribute
  350. // Non-standard
  351. enum { "on", "off" } autocomplete,
  352. string placeholder,
  353. // HTML
  354. string accept, enum { "left", "right", "top", "middle", "bottom" } align,
  355. string alt, bool checked, bool disabled, int maxlength, string name,
  356. bool readonly, int size, string src,
  357. enum {
  358. "button", "checkbox", "file", "hidden", "image", "password", "radio",
  359. "reset", "submit", "text"
  360. } type,
  361. string value;
  362. category %flow, %phrase, %interactive;
  363. protected $tagName = 'input';
  364. }
  365. class :ins extends :xhp:html-element {
  366. attribute string cite, string datetime;
  367. category %flow, %phrase;
  368. // transparent
  369. children (pcdata | %flow)*;
  370. protected $tagName = 'ins';
  371. }
  372. class :kbd extends :xhp:html-element {
  373. category %flow, %phrase;
  374. children (pcdata | %phrase)*;
  375. protected $tagName = 'kbd';
  376. }
  377. class :label extends :xhp:html-element {
  378. attribute string for;
  379. category %flow, %phrase, %interactive;
  380. // may not contain label
  381. children (pcdata | %phrase)*;
  382. protected $tagName = 'label';
  383. }
  384. class :legend extends :xhp:html-element {
  385. children (pcdata | %phrase)*;
  386. protected $tagName = 'legend';
  387. }
  388. class :li extends :xhp:html-element {
  389. children (pcdata | %flow)*;
  390. protected $tagName = 'li';
  391. }
  392. class :link extends :xhp:html-singleton {
  393. attribute
  394. string charset, string href, string hreflang, string media, string rel,
  395. string rev, string target, string type;
  396. category %metadata;
  397. protected $tagName = 'link';
  398. }
  399. class :map extends :xhp:html-element {
  400. attribute string name;
  401. category %flow, %phrase;
  402. // transparent
  403. children ((pcdata | %flow)+ | :area+);
  404. protected $tagName = 'map';
  405. }
  406. class :meta extends :xhp:html-singleton {
  407. attribute
  408. string content @required,
  409. enum {
  410. "content-type", "content-style-type", "expires", "refresh", "set-cookie"
  411. } http-equiv,
  412. string http-equiv, string name, string scheme;
  413. category %metadata;
  414. protected $tagName = 'meta';
  415. }
  416. class :noframes extends :xhp:html-element {
  417. children (%html-body);
  418. protected $tagName = 'noframes';
  419. }
  420. class :noscript extends :xhp:html-element {
  421. // transparent
  422. category %flow, %phrase;
  423. protected $tagName = 'noscript';
  424. }
  425. class :object extends :xhp:html-element {
  426. attribute
  427. enum { "left", "right", "top", "bottom" } align, string archive, int border,
  428. string classid, string codebase, string codetype, string data, bool declare,
  429. int height, int hspace, string name, string standby, string type,
  430. string usemap, int vspace, int width;
  431. category %flow, %phrase;
  432. // transparent, after the params
  433. children (:param*, (pcdata | %flow)*);
  434. protected $tagName = 'object';
  435. }
  436. class :ol extends :xhp:html-element {
  437. category %flow;
  438. children (:li)*;
  439. protected $tagName = 'ol';
  440. }
  441. class :optgroup extends :xhp:html-element {
  442. attribute string label, bool disabled;
  443. children (:option)*;
  444. protected $tagName = 'optgroup';
  445. }
  446. class :option extends :xhp:pseudo-singleton {
  447. attribute bool disabled, string label, bool selected, string value;
  448. protected $tagName = 'option';
  449. }
  450. class :p extends :xhp:html-element {
  451. category %flow;
  452. children (pcdata | %phrase)*;
  453. protected $tagName = 'p';
  454. }
  455. class :param extends :xhp:pseudo-singleton {
  456. attribute
  457. string name, string type, string value,
  458. enum { "data", "ref", "object" } valuetype;
  459. protected $tagName = 'param';
  460. }
  461. class :pre extends :xhp:html-element {
  462. category %flow;
  463. children (pcdata | %phrase)*;
  464. protected $tagName = 'pre';
  465. }
  466. class :q extends :xhp:html-element {
  467. attribute string cite;
  468. category %flow, %phrase;
  469. children (pcdata | %phrase)*;
  470. protected $tagName = 'q';
  471. }
  472. // deprecated
  473. class :s extends :xhp:html-element {
  474. category %flow, %phrase;
  475. children (pcdata | %phrase)*;
  476. protected $tagName = 's';
  477. }
  478. class :samp extends :xhp:html-element {
  479. category %flow, %phrase;
  480. children (pcdata | %phrase)*;
  481. protected $tagName = 'samp';
  482. }
  483. class :script extends :xhp:pseudo-singleton {
  484. attribute string charset, bool defer, string src, string type;
  485. category %flow, %phrase, %metadata;
  486. protected $tagName = 'script';
  487. }
  488. class :select extends :xhp:html-element {
  489. attribute bool disabled, bool multiple, string name, int size;
  490. category %flow, %phrase, %interactive;
  491. children (:option | :optgroup)*;
  492. protected $tagName = 'select';
  493. }
  494. class :small extends :xhp:html-element {
  495. category %flow, %phrase;
  496. children (pcdata | %phrase)*;
  497. protected $tagName = 'small';
  498. }
  499. class :span extends :xhp:html-element {
  500. category %flow, %phrase;
  501. children (pcdata | %phrase)*;
  502. protected $tagName = 'span';
  503. }
  504. class :strong extends :xhp:html-element {
  505. category %flow, %phrase;
  506. children (pcdata | %phrase)*;
  507. protected $tagName = 'strong';
  508. }
  509. class :style extends :xhp:pseudo-singleton {
  510. attribute
  511. enum {
  512. "screen", "tty", "tv", "projection", "handheld", "print", "braille",
  513. "aural", "all"
  514. } media, string type;
  515. category %metadata;
  516. protected $tagName = 'style';
  517. }
  518. class :sub extends :xhp:html-element {
  519. category %flow, %phrase;
  520. children (pcdata | %phrase);
  521. protected $tagName = 'sub';
  522. }
  523. class :sup extends :xhp:html-element {
  524. category %flow, %phrase;
  525. children (pcdata | %phrase);
  526. protected $tagName = 'sup';
  527. }
  528. class :table extends :xhp:html-element {
  529. attribute
  530. int border, int cellpadding, int cellspacing,
  531. enum {
  532. "void", "above", "below", "hsides", "lhs", "rhs", "vsides", "box",
  533. "border"
  534. } frame,
  535. enum { "none", "groups", "rows", "cols", "all" } rules,
  536. string summary, string width;
  537. category %flow;
  538. children (
  539. :caption?, :colgroup*,
  540. :thead?,
  541. (
  542. (:tfoot, (:tbody+ | :tr*)) |
  543. ((:tbody+ | :tr*), :tfoot?)
  544. )
  545. );
  546. protected $tagName = 'table';
  547. }
  548. class :tbody extends :xhp:html-element {
  549. attribute
  550. enum { "right", "left", "center", "justify", "char" } align, string char,
  551. int charoff, enum { "top", "middle", "bottom", "baseline" } valign;
  552. children (:tr)*;
  553. protected $tagName = 'tbody';
  554. }
  555. class :td extends :xhp:html-element {
  556. attribute
  557. string abbr, enum { "left", "right", "center", "justify", "char" } align,
  558. string axis, string char, int charoff, int colspan, string headers,
  559. int rowspan, enum { "col", "colgroup", "row", "rowgroup" } scope,
  560. enum { "top", "middle", "bottom", "baseline" } valign;
  561. children (pcdata | %flow)*;
  562. protected $tagName = 'td';
  563. }
  564. class :textarea extends :xhp:pseudo-singleton {
  565. attribute int cols, int rows, bool disabled, string name, bool readonly;
  566. category %flow, %phrase, %interactive;
  567. protected $tagName = 'textarea';
  568. }
  569. class :tfoot extends :xhp:html-element {
  570. attribute
  571. enum { "left", "right", "center", "justify", "char" } align, string char,
  572. int charoff, enum { "top", "middle", "bottom", "baseline" } valign;
  573. children (:tr)*;
  574. protected $tagName = 'tfoot';
  575. }
  576. class :th extends :xhp:html-element {
  577. attribute
  578. string abbr, enum { "left", "right", "center", "justify", "char" } align,
  579. string axis, string char, int charoff, int colspan, int rowspan,
  580. enum { "col", "colgroup", "row", "rowgroup" } scope,
  581. enum { "top", "middle", "bottom", "baseline" } valign;
  582. children (pcdata | %flow)*;
  583. protected $tagName = 'th';
  584. }
  585. class :thead extends :xhp:html-element {
  586. attribute
  587. enum { "left", "right", "center", "justify", "char" } align, string char,
  588. int charoff, enum { "top", "middle", "bottom", "baseline" } valign;
  589. children (:tr)*;
  590. protected $tagName = 'thead';
  591. }
  592. class :title extends :xhp:pseudo-singleton {
  593. // also a member of "metadata", but is not listed here. see comments in :head
  594. // for more information.
  595. protected $tagName = 'title';
  596. }
  597. class :tr extends :xhp:html-element {
  598. attribute
  599. enum { "left", "right", "center", "justify", "char" } align, string char,
  600. int charoff, enum { "top", "middle", "bottom", "baseline" } valign;
  601. children (:th | :td)*;
  602. protected $tagName = 'tr';
  603. }
  604. class :tt extends :xhp:html-element {
  605. category %flow, %phrase;
  606. children (pcdata | %phrase)*;
  607. protected $tagName = 'tt';
  608. }
  609. // deprecated
  610. class :u extends :xhp:html-element {
  611. category %flow, %phrase;
  612. children (pcdata | %phrase)*;
  613. protected $tagName = 'u';
  614. }
  615. class :ul extends :xhp:html-element {
  616. category %flow;
  617. children (:li)*;
  618. protected $tagName = 'ul';
  619. }
  620. class :var extends :xhp:html-element {
  621. category %flow, %phrase;
  622. children (pcdata | %phrase)*;
  623. protected $tagName = 'var';
  624. }
  625. /**
  626. * Render an <html /> element with a DOCTYPE, great for dumping a page to a
  627. * browser. Choose from a wide variety of flavors like XHTML 1.0 Strict, HTML
  628. * 4.01 Transitional, and new and improved HTML 5!
  629. *
  630. * Note: Some flavors may not be available in your area.
  631. */
  632. class :x:doctype extends :x:primitive {
  633. children (:html);
  634. protected function stringify() {
  635. $children = $this->getChildren();
  636. return '<!DOCTYPE html>' . (:x:base::renderChild($children[0]));
  637. }
  638. }