4
* This file is part of the Nette Framework (http://nette.org)
6
* Copyright (c) 2004 David Grudl (http://davidgrudl.com)
8
* For the full copyright and license information, please view
9
* the file license.txt that was distributed with this source code.
12
namespace Nette\Forms\Rendering;
19
* Converts a Form into the HTML output.
23
class DefaultFormRenderer extends Nette\Object implements Nette\Forms\IFormRenderer
28
* /--- if (form.errors) error.container
29
* .... error.item [.class]
32
* /--- hidden.container
33
* .... HIDDEN CONTROLS
36
* /--- group.container
38
* .... group.description
40
* /--- controls.container
42
* /--- pair.container [.required .optional .odd]
44
* /--- label.container
47
* .... label.requiredsuffix
50
* /--- control.container [.odd]
51
* .... CONTROL [.required .text .password .file .submit .button]
52
* .... control.requiredsuffix
53
* .... control.description
54
* .... if (control.errors) error.container
61
* @var array of HTML tags */
62
public $wrappers = array(
69
'container' => 'ul class=error',
74
'container' => 'fieldset',
80
'container' => 'table',
85
'.required' => 'required',
95
'description' => 'small',
96
'requiredsuffix' => '',
98
'.required' => 'required',
100
'.password' => 'text',
102
'.submit' => 'button',
103
'.image' => 'imagebutton',
104
'.button' => 'button',
110
'requiredsuffix' => '',
114
'container' => 'div',
118
/** @var Nette\Forms\Form */
126
* Provides complete form rendering.
127
* @param Nette\Forms\Form
128
* @param string 'begin', 'errors', 'body', 'end' or empty to render all
131
public function render(Nette\Forms\Form $form, $mode = NULL)
133
if ($this->form !== $form) {
139
if (!$mode || $mode === 'begin') {
140
$s .= $this->renderBegin();
142
if ((!$mode && $this->getValue('form errors')) || $mode === 'errors') {
143
$s .= $this->renderErrors();
145
if (!$mode || $mode === 'body') {
146
$s .= $this->renderBody();
148
if (!$mode || $mode === 'end') {
149
$s .= $this->renderEnd();
156
public function setClientScript()
158
trigger_error(__METHOD__ . '() is deprecated; use unobstructive JavaScript instead.', E_USER_WARNING);
167
protected function init()
169
// TODO: only for back compatiblity - remove?
170
$wrapper = & $this->wrappers['control'];
171
foreach ($this->form->getControls() as $control) {
172
if ($control->isRequired() && isset($wrapper['.required'])) {
173
$control->getLabelPrototype()->class($wrapper['.required'], TRUE);
176
$el = $control->getControlPrototype();
177
if ($el->getName() === 'input' && isset($wrapper['.' . $el->type])) {
178
$el->class($wrapper['.' . $el->type], TRUE);
185
* Renders form begin.
188
public function renderBegin()
192
foreach ($this->form->getControls() as $control) {
193
$control->setOption('rendered', FALSE);
196
if (strcasecmp($this->form->getMethod(), 'get') === 0) {
197
$el = clone $this->form->getElementPrototype();
198
$url = explode('?', (string) $el->action, 2);
199
$el->action = $url[0];
201
if (isset($url[1])) {
202
foreach (preg_split('#[;&]#', $url[1]) as $param) {
203
$parts = explode('=', $param, 2);
204
$name = urldecode($parts[0]);
205
if (!isset($this->form[$name])) {
206
$s .= Html::el('input', array('type' => 'hidden', 'name' => $name, 'value' => urldecode($parts[1])));
209
$s = "\n\t" . $this->getWrapper('hidden container')->setHtml($s);
211
return $el->startTag() . $s;
215
return $this->form->getElementPrototype()->startTag();
224
public function renderEnd()
227
foreach ($this->form->getControls() as $control) {
228
if ($control instanceof Nette\Forms\Controls\HiddenField && !$control->getOption('rendered')) {
229
$s .= (string) $control->getControl();
232
if (iterator_count($this->form->getComponents(TRUE, 'Nette\Forms\Controls\TextInput')) < 2) {
233
$s .= '<!--[if IE]><input type=IEbug disabled style="display:none"><![endif]-->';
236
$s = $this->getWrapper('hidden container')->setHtml($s) . "\n";
239
return $s . $this->form->getElementPrototype()->endTag() . "\n";
244
* Renders validation errors (per form or per control).
247
public function renderErrors(Nette\Forms\IControl $control = NULL)
249
$errors = $control === NULL ? $this->form->getErrors() : $control->getErrors();
250
if (count($errors)) {
251
$ul = $this->getWrapper('error container');
252
$li = $this->getWrapper('error item');
254
foreach ($errors as $error) {
256
if ($error instanceof Html) {
259
$item->setText($error);
263
return "\n" . $ul->render(0);
272
public function renderBody()
276
$defaultContainer = $this->getWrapper('group container');
277
$translator = $this->form->getTranslator();
279
foreach ($this->form->getGroups() as $group) {
280
if (!$group->getControls() || !$group->getOption('visual')) {
284
$container = $group->getOption('container', $defaultContainer);
285
$container = $container instanceof Html ? clone $container : Html::el($container);
287
$s .= "\n" . $container->startTag();
289
$text = $group->getOption('label');
290
if ($text instanceof Html) {
293
} elseif (is_string($text)) {
294
if ($translator !== NULL) {
295
$text = $translator->translate($text);
297
$s .= "\n" . $this->getWrapper('group label')->setText($text) . "\n";
300
$text = $group->getOption('description');
301
if ($text instanceof Html) {
304
} elseif (is_string($text)) {
305
if ($translator !== NULL) {
306
$text = $translator->translate($text);
308
$s .= $this->getWrapper('group description')->setText($text) . "\n";
311
$s .= $this->renderControls($group);
313
$remains = $container->endTag() . "\n" . $remains;
314
if (!$group->getOption('embedNext')) {
320
$s .= $remains . $this->renderControls($this->form);
322
$container = $this->getWrapper('form container');
323
$container->setHtml($s);
324
return $container->render(0);
329
* Renders group of controls.
330
* @param Nette\Forms\Container|FormGroup
333
public function renderControls($parent)
335
if (!($parent instanceof Nette\Forms\Container || $parent instanceof Nette\Forms\ControlGroup)) {
336
throw new Nette\InvalidArgumentException("Argument must be FormContainer or FormGroup instance.");
339
$container = $this->getWrapper('controls container');
342
foreach ($parent->getControls() as $control) {
343
if ($control->getOption('rendered') || $control instanceof Nette\Forms\Controls\HiddenField || $control->getForm(FALSE) !== $this->form) {
346
} elseif ($control instanceof Nette\Forms\Controls\Button) {
347
$buttons[] = $control;
351
$container->add($this->renderPairMulti($buttons));
354
$container->add($this->renderPair($control));
359
$container->add($this->renderPairMulti($buttons));
363
if (count($container)) {
364
$s .= "\n" . $container . "\n";
372
* Renders single visual row.
375
public function renderPair(Nette\Forms\IControl $control)
377
$pair = $this->getWrapper('pair container');
378
$pair->add($this->renderLabel($control));
379
$pair->add($this->renderControl($control));
380
$pair->class($this->getValue($control->isRequired() ? 'pair .required' : 'pair .optional'), TRUE);
381
$pair->class($control->getOption('class'), TRUE);
382
if (++$this->counter % 2) {
383
$pair->class($this->getValue('pair .odd'), TRUE);
385
$pair->id = $control->getOption('id');
386
return $pair->render(0);
391
* Renders single visual row of multiple controls.
392
* @param IFormControl[]
395
public function renderPairMulti(array $controls)
398
foreach ($controls as $control) {
399
if (!$control instanceof Nette\Forms\IControl) {
400
throw new Nette\InvalidArgumentException("Argument must be array of IFormControl instances.");
402
$s[] = (string) $control->getControl();
404
$pair = $this->getWrapper('pair container');
405
$pair->add($this->renderLabel($control));
406
$pair->add($this->getWrapper('control container')->setHtml(implode(" ", $s)));
407
return $pair->render(0);
412
* Renders 'label' part of visual row of controls.
415
public function renderLabel(Nette\Forms\IControl $control)
417
$head = $this->getWrapper('label container');
419
if ($control instanceof Nette\Forms\Controls\Checkbox || $control instanceof Nette\Forms\Controls\Button) {
420
return $head->setHtml(($head->getName() === 'td' || $head->getName() === 'th') ? ' ' : '');
423
$label = $control->getLabel();
424
$suffix = $this->getValue('label suffix') . ($control->isRequired() ? $this->getValue('label requiredsuffix') : '');
425
if ($label instanceof Html) {
426
$label->setHtml($label->getHtml() . $suffix);
429
return $head->setHtml((string) $label . $suffix);
435
* Renders 'control' part of visual row of controls.
438
public function renderControl(Nette\Forms\IControl $control)
440
$body = $this->getWrapper('control container');
441
if ($this->counter % 2) {
442
$body->class($this->getValue('control .odd'), TRUE);
445
$description = $control->getOption('description');
446
if ($description instanceof Html) {
447
$description = ' ' . $control->getOption('description');
449
} elseif (is_string($description)) {
450
$description = ' ' . $this->getWrapper('control description')->setText($control->translate($description));
456
if ($control->isRequired()) {
457
$description = $this->getValue('control requiredsuffix') . $description;
460
if ($this->getValue('control errors')) {
461
$description .= $this->renderErrors($control);
464
if ($control instanceof Nette\Forms\Controls\Checkbox || $control instanceof Nette\Forms\Controls\Button) {
465
return $body->setHtml((string) $control->getControl() . (string) $control->getLabel() . $description);
468
return $body->setHtml((string) $control->getControl() . $description);
475
* @return Nette\Utils\Html
477
protected function getWrapper($name)
479
$data = $this->getValue($name);
480
return $data instanceof Html ? clone $data : Html::el($data);
488
protected function getValue($name)
490
$name = explode(' ', $name);
491
$data = & $this->wrappers[$name[0]][$name[1]];