PageRenderTime 40ms CodeModel.GetById 9ms RepoModel.GetById 0ms app.codeStats 0ms

/shopaholic/lib/Nette/Forms/Renderers/ConventionalRenderer.php

http://github.com/jakubkulhan/shopaholic
PHP | 515 lines | 280 code | 102 blank | 133 comment | 51 complexity | d2d2d50dbb2cbabdc085e49efed11591 MD5 | raw file
Possible License(s): WTFPL
  1. <?php
  2. /**
  3. * Nette Framework
  4. *
  5. * Copyright (c) 2004, 2009 David Grudl (http://davidgrudl.com)
  6. *
  7. * This source file is subject to the "Nette license" that is bundled
  8. * with this package in the file license.txt.
  9. *
  10. * For more information please see http://nettephp.com
  11. *
  12. * @copyright Copyright (c) 2004, 2009 David Grudl
  13. * @license http://nettephp.com/license Nette license
  14. * @link http://nettephp.com
  15. * @category Nette
  16. * @package Nette\Forms
  17. * @version $Id: ConventionalRenderer.php 235 2009-03-26 22:08:52Z david@grudl.com $
  18. */
  19. require_once dirname(__FILE__) . '/../../Object.php';
  20. require_once dirname(__FILE__) . '/../../Forms/IFormRenderer.php';
  21. /**
  22. * Converts a Form into the HTML output.
  23. *
  24. * @author David Grudl
  25. * @copyright Copyright (c) 2004, 2009 David Grudl
  26. * @package Nette\Forms
  27. */
  28. class ConventionalRenderer extends Object implements IFormRenderer
  29. {
  30. /**
  31. * /--- form.container
  32. *
  33. * /--- if (form.errors) error.container
  34. * .... error.item [.class]
  35. * \---
  36. *
  37. * /--- hidden.container
  38. * .... HIDDEN CONTROLS
  39. * \---
  40. *
  41. * /--- group.container
  42. * .... group.label
  43. * .... group.description
  44. *
  45. * /--- controls.container
  46. *
  47. * /--- pair.container [.required .optional .odd]
  48. *
  49. * /--- label.container
  50. * .... LABEL
  51. * .... label.suffix
  52. * \---
  53. *
  54. * /--- control.container [.odd]
  55. * .... CONTROL [.required .text .password .file .submit .button]
  56. * .... control.description
  57. * .... if (control.errors) error.container
  58. * \---
  59. * \---
  60. * \---
  61. * \---
  62. * \--
  63. *
  64. * @var array of HTML tags */
  65. public $wrappers = array(
  66. 'form' => array(
  67. 'container' => NULL,
  68. 'errors' => TRUE,
  69. ),
  70. 'error' => array(
  71. 'container' => 'ul class=error',
  72. 'item' => 'li',
  73. ),
  74. 'group' => array(
  75. 'container' => 'fieldset',
  76. 'label' => 'legend',
  77. 'description' => 'p',
  78. ),
  79. 'controls' => array(
  80. 'container' => 'table',
  81. ),
  82. 'pair' => array(
  83. 'container' => 'tr',
  84. '.required' => 'required',
  85. '.optional' => NULL,
  86. '.odd' => NULL,
  87. ),
  88. 'control' => array(
  89. 'container' => 'td',
  90. '.odd' => NULL,
  91. 'errors' => FALSE,
  92. 'description' => 'small',
  93. '.required' => 'required',
  94. '.text' => 'text',
  95. '.password' => 'text',
  96. '.file' => 'text',
  97. '.submit' => 'button',
  98. '.image' => 'imagebutton',
  99. '.button' => 'button',
  100. ),
  101. 'label' => array(
  102. 'container' => 'th',
  103. 'suffix' => NULL,
  104. ),
  105. 'hidden' => array(
  106. 'container' => 'div',
  107. ),
  108. );
  109. /** @var Form */
  110. protected $form;
  111. /** @var object */
  112. protected $clientScript = TRUE; // means autodetect
  113. /** @var int */
  114. protected $counter;
  115. /**
  116. * Provides complete form rendering.
  117. * @param Form
  118. * @param string
  119. * @return string
  120. */
  121. public function render(Form $form, $mode = NULL)
  122. {
  123. if ($this->form !== $form) {
  124. $this->form = $form;
  125. $this->init();
  126. }
  127. $s = '';
  128. if (!$mode || $mode === 'begin') {
  129. $s .= $this->renderBegin();
  130. }
  131. if ((!$mode && $this->getValue('form errors')) || $mode === 'errors') {
  132. $s .= $this->renderErrors();
  133. }
  134. if (!$mode || $mode === 'body') {
  135. $s .= $this->renderBody();
  136. }
  137. if (!$mode || $mode === 'end') {
  138. $s .= $this->renderEnd();
  139. }
  140. return $s;
  141. }
  142. /**
  143. * Sets JavaScript handler.
  144. * @param object
  145. * @return void
  146. */
  147. public function setClientScript($clientScript = NULL)
  148. {
  149. $this->clientScript = $clientScript;
  150. }
  151. /**
  152. * Returns JavaScript handler.
  153. * @return mixed
  154. */
  155. public function getClientScript()
  156. {
  157. if ($this->clientScript === TRUE) {
  158. $this->clientScript = new InstantClientScript($this->form);
  159. }
  160. return $this->clientScript;
  161. }
  162. /**
  163. * Initializes form.
  164. * @return void
  165. */
  166. protected function init()
  167. {
  168. $clientScript = $this->getClientScript();
  169. if ($clientScript !== NULL) {
  170. $clientScript->enable();
  171. }
  172. // TODO: only for back compatiblity - remove?
  173. $wrapper = & $this->wrappers['control'];
  174. foreach ($this->form->getControls() as $control) {
  175. if ($control->getOption('required') && isset($wrapper['.required'])) {
  176. $control->getLabelPrototype()->class($wrapper['.required'], TRUE);
  177. }
  178. $el = $control->getControlPrototype();
  179. if ($el->getName() === 'input' && isset($wrapper['.' . $el->type])) {
  180. $el->class($wrapper['.' . $el->type], TRUE);
  181. }
  182. }
  183. }
  184. /**
  185. * Renders form begin.
  186. * @return string
  187. */
  188. public function renderBegin()
  189. {
  190. $this->counter = 0;
  191. foreach ($this->form->getControls() as $control) {
  192. $control->setOption('rendered', FALSE);
  193. }
  194. return $this->form->getElementPrototype()->startTag();
  195. }
  196. /**
  197. * Renders form end.
  198. * @return string
  199. */
  200. public function renderEnd()
  201. {
  202. $s = '';
  203. foreach ($this->form->getControls() as $control) {
  204. if ($control instanceof HiddenField && !$control->getOption('rendered')) {
  205. $s .= (string) $control->getControl();
  206. }
  207. }
  208. if ($s) {
  209. $s = $this->getWrapper('hidden container')->setHtml($s) . "\n";
  210. }
  211. $s .= $this->form->getElementPrototype()->endTag() . "\n";
  212. $clientScript = $this->getClientScript();
  213. if ($clientScript !== NULL) {
  214. $s .= $clientScript->renderClientScript() . "\n";
  215. }
  216. return $s;
  217. }
  218. /**
  219. * Renders validation errors (per form or per control).
  220. * @param IFormControl
  221. * @return string
  222. */
  223. public function renderErrors(IFormControl $control = NULL)
  224. {
  225. $errors = $control === NULL ? $this->form->getErrors() : $control->getErrors();
  226. if (count($errors)) {
  227. $ul = $this->getWrapper('error container');
  228. $li = $this->getWrapper('error item');
  229. foreach ($errors as $error) {
  230. $item = clone $li;
  231. if ($error instanceof Html) {
  232. $item->add($error);
  233. } else {
  234. $item->setText($error);
  235. }
  236. $ul->add($item);
  237. }
  238. return "\n" . $ul->render(0);
  239. }
  240. }
  241. /**
  242. * Renders form body.
  243. * @return string
  244. */
  245. public function renderBody()
  246. {
  247. $s = $remains = '';
  248. $defaultContainer = $this->getWrapper('group container');
  249. $translator = $this->form->getTranslator();
  250. foreach ($this->form->getGroups() as $group) {
  251. if (!$group->getControls() || !$group->getOption('visual')) continue;
  252. $container = $group->getOption('container', $defaultContainer);
  253. $container = $container instanceof Html ? clone $container : Html::el($container);
  254. $s .= "\n" . $container->startTag();
  255. $text = $group->getOption('label');
  256. if ($text instanceof Html) {
  257. $s .= $text;
  258. } elseif (is_string($text)) {
  259. if ($translator !== NULL) {
  260. $text = $translator->translate($text);
  261. }
  262. $s .= "\n" . $this->getWrapper('group label')->setText($text) . "\n";
  263. }
  264. $text = $group->getOption('description');
  265. if ($text instanceof Html) {
  266. $s .= $text;
  267. } elseif (is_string($text)) {
  268. if ($translator !== NULL) {
  269. $text = $translator->translate($text);
  270. }
  271. $s .= $this->getWrapper('group description')->setText($text) . "\n";
  272. }
  273. $s .= $this->renderControls($group);
  274. $remains = $container->endTag() . "\n" . $remains;
  275. if (!$group->getOption('embedNext')) {
  276. $s .= $remains;
  277. $remains = '';
  278. }
  279. }
  280. $s .= $remains . $this->renderControls($this->form);
  281. $container = $this->getWrapper('form container');
  282. $container->setHtml($s);
  283. return $container->render(0);
  284. }
  285. /**
  286. * Renders group of controls.
  287. * @param FormContainer|FormGroup
  288. * @return string
  289. */
  290. public function renderControls($parent)
  291. {
  292. if (!($parent instanceof FormContainer || $parent instanceof FormGroup)) {
  293. throw new InvalidArgumentException("Argument must be FormContainer or FormGroup instance.");
  294. }
  295. $container = $this->getWrapper('controls container');
  296. $buttons = NULL;
  297. foreach ($parent->getControls() as $control) {
  298. if ($control->getOption('rendered') || $control instanceof HiddenField || $control->getForm(FALSE) !== $this->form) {
  299. // skip
  300. } elseif ($control instanceof Button) {
  301. $buttons[] = $control;
  302. } else {
  303. if ($buttons) {
  304. $container->add($this->renderPairMulti($buttons));
  305. $buttons = NULL;
  306. }
  307. $container->add($this->renderPair($control));
  308. }
  309. }
  310. if ($buttons) {
  311. $container->add($this->renderPairMulti($buttons));
  312. }
  313. $s = '';
  314. if (count($container)) {
  315. $s .= "\n" . $container . "\n";
  316. }
  317. return $s;
  318. }
  319. /**
  320. * Renders single visual row.
  321. * @param IFormControl
  322. * @return string
  323. */
  324. public function renderPair(IFormControl $control)
  325. {
  326. $pair = $this->getWrapper('pair container');
  327. $pair->add($this->renderLabel($control));
  328. $pair->add($this->renderControl($control));
  329. $pair->class($this->getValue($control->getOption('required') ? 'pair .required' : 'pair .optional'), TRUE);
  330. $pair->class($control->getOption('class'), TRUE);
  331. if (++$this->counter % 2) $pair->class($this->getValue('pair .odd'), TRUE);
  332. $pair->id = $control->getOption('id');
  333. return $pair->render(0);
  334. }
  335. /**
  336. * Renders single visual row of multiple controls.
  337. * @param array of IFormControl
  338. * @return string
  339. */
  340. public function renderPairMulti(array $controls)
  341. {
  342. $s = array();
  343. foreach ($controls as $control) {
  344. if (!($control instanceof IFormControl)) {
  345. throw new InvalidArgumentException("Argument must be array of IFormControl instances.");
  346. }
  347. $s[] = (string) $control->getControl();
  348. }
  349. $pair = $this->getWrapper('pair container');
  350. $pair->add($this->getWrapper('label container')->setHtml('&nbsp;'));
  351. $pair->add($this->getWrapper('control container')->setHtml(implode(" ", $s)));
  352. return $pair->render(0);
  353. }
  354. /**
  355. * Renders 'label' part of visual row of controls.
  356. * @param IFormControl
  357. * @return string
  358. */
  359. public function renderLabel(IFormControl $control)
  360. {
  361. $head = $this->getWrapper('label container');
  362. if ($control instanceof Checkbox || $control instanceof Button) {
  363. return $head->setHtml('&nbsp;');
  364. } else {
  365. return $head->setHtml((string) $control->getLabel() . $this->getValue('label suffix'));
  366. }
  367. }
  368. /**
  369. * Renders 'control' part of visual row of controls.
  370. * @param IFormControl
  371. * @return string
  372. */
  373. public function renderControl(IFormControl $control)
  374. {
  375. $body = $this->getWrapper('control container');
  376. if ($this->counter % 2) $body->class($this->getValue('control .odd'), TRUE);
  377. $description = $control->getOption('description');
  378. if ($description instanceof Html) {
  379. $description = ' ' . $control->getOption('description');
  380. } elseif (is_string($description)) {
  381. $description = ' ' . $this->getWrapper('control description')->setText($description);
  382. } else {
  383. $description = '';
  384. }
  385. if ($this->getValue('control errors')) {
  386. $description .= $this->renderErrors($control);
  387. }
  388. if ($control instanceof Checkbox || $control instanceof Button) {
  389. return $body->setHtml((string) $control->getControl() . (string) $control->getLabel() . $description);
  390. } else {
  391. return $body->setHtml((string) $control->getControl() . $description);
  392. }
  393. }
  394. /**
  395. * @param string
  396. * @return Html
  397. */
  398. protected function getWrapper($name)
  399. {
  400. $data = $this->getValue($name);
  401. return $data instanceof Html ? clone $data : Html::el($data);
  402. }
  403. /**
  404. * @param string
  405. * @return string
  406. */
  407. protected function getValue($name)
  408. {
  409. $name = explode(' ', $name);
  410. $data = & $this->wrappers[$name[0]][$name[1]];
  411. return $data;
  412. }
  413. }