1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Nette\Forms\Rendering;
9:
10: use Nette,
11: Nette\Utils\Html;
12:
13:
14: 15: 16: 17: 18:
19: class DefaultFormRenderer extends Nette\Object implements Nette\Forms\IFormRenderer
20: {
21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57:
58: public $wrappers = array(
59: 'form' => array(
60: 'container' => NULL,
61: 'errors' => TRUE,
62: ),
63:
64: 'error' => array(
65: 'container' => 'ul class=error',
66: 'item' => 'li',
67: ),
68:
69: 'group' => array(
70: 'container' => 'fieldset',
71: 'label' => 'legend',
72: 'description' => 'p',
73: ),
74:
75: 'controls' => array(
76: 'container' => 'table',
77: ),
78:
79: 'pair' => array(
80: 'container' => 'tr',
81: '.required' => 'required',
82: '.optional' => NULL,
83: '.odd' => NULL,
84: ),
85:
86: 'control' => array(
87: 'container' => 'td',
88: '.odd' => NULL,
89:
90: 'errors' => FALSE,
91: 'description' => 'small',
92: 'requiredsuffix' => '',
93:
94: '.required' => 'required',
95: '.text' => 'text',
96: '.password' => 'text',
97: '.file' => 'text',
98: '.submit' => 'button',
99: '.image' => 'imagebutton',
100: '.button' => 'button',
101: ),
102:
103: 'label' => array(
104: 'container' => 'th',
105: 'suffix' => NULL,
106: 'requiredsuffix' => '',
107: ),
108:
109: 'hidden' => array(
110: 'container' => 'div',
111: ),
112: );
113:
114:
115: protected $form;
116:
117:
118: protected $counter;
119:
120:
121: 122: 123: 124: 125: 126:
127: public function render(Nette\Forms\Form $form, $mode = NULL)
128: {
129: if ($this->form !== $form) {
130: $this->form = $form;
131: $this->init();
132: }
133:
134: $s = '';
135: if (!$mode || $mode === 'begin') {
136: $s .= $this->renderBegin();
137: }
138: if ((!$mode && $this->getValue('form errors')) || $mode === 'errors') {
139: $s .= $this->renderErrors();
140: }
141: if (!$mode || $mode === 'body') {
142: $s .= $this->renderBody();
143: }
144: if (!$mode || $mode === 'end') {
145: $s .= $this->renderEnd();
146: }
147: return $s;
148: }
149:
150:
151:
152: public function setClientScript()
153: {
154: trigger_error(__METHOD__ . '() is deprecated; use unobstructive JavaScript instead.', E_USER_WARNING);
155: return $this;
156: }
157:
158:
159: 160: 161: 162:
163: protected function init()
164: {
165:
166: $wrapper = & $this->wrappers['control'];
167: foreach ($this->form->getControls() as $control) {
168: if ($control->isRequired() && isset($wrapper['.required'])) {
169: $control->getLabelPrototype()->class($wrapper['.required'], TRUE);
170: }
171:
172: $el = $control->getControlPrototype();
173: if ($el->getName() === 'input' && isset($wrapper['.' . $el->type])) {
174: $el->class($wrapper['.' . $el->type], TRUE);
175: }
176: }
177: }
178:
179:
180: 181: 182: 183:
184: public function renderBegin()
185: {
186: $this->counter = 0;
187:
188: foreach ($this->form->getControls() as $control) {
189: $control->setOption('rendered', FALSE);
190: }
191:
192: if (strcasecmp($this->form->getMethod(), 'get') === 0) {
193: $el = clone $this->form->getElementPrototype();
194: $url = explode('?', (string) $el->action, 2);
195: $el->action = $url[0];
196: $s = '';
197: if (isset($url[1])) {
198: foreach (preg_split('#[;&]#', $url[1]) as $param) {
199: $parts = explode('=', $param, 2);
200: $name = urldecode($parts[0]);
201: if (!isset($this->form[$name])) {
202: $s .= Html::el('input', array('type' => 'hidden', 'name' => $name, 'value' => urldecode($parts[1])));
203: }
204: }
205: $s = "\n\t" . $this->getWrapper('hidden container')->setHtml($s);
206: }
207: return $el->startTag() . $s;
208:
209:
210: } else {
211: return $this->form->getElementPrototype()->startTag();
212: }
213: }
214:
215:
216: 217: 218: 219:
220: public function renderEnd()
221: {
222: $s = '';
223: foreach ($this->form->getControls() as $control) {
224: if ($control instanceof Nette\Forms\Controls\HiddenField && !$control->getOption('rendered')) {
225: $s .= (string) $control->getControl();
226: }
227: }
228: if (iterator_count($this->form->getComponents(TRUE, 'Nette\Forms\Controls\TextInput')) < 2) {
229: $s .= '<!--[if IE]><input type=IEbug disabled style="display:none"><![endif]-->';
230: }
231: if ($s) {
232: $s = $this->getWrapper('hidden container')->setHtml($s) . "\n";
233: }
234:
235: return $s . $this->form->getElementPrototype()->endTag() . "\n";
236: }
237:
238:
239: 240: 241: 242:
243: public function renderErrors(Nette\Forms\IControl $control = NULL)
244: {
245: $errors = $control === NULL ? $this->form->getErrors() : $control->getErrors();
246: if (count($errors)) {
247: $ul = $this->getWrapper('error container');
248: $li = $this->getWrapper('error item');
249:
250: foreach ($errors as $error) {
251: $item = clone $li;
252: if ($error instanceof Html) {
253: $item->add($error);
254: } else {
255: $item->setText($error);
256: }
257: $ul->add($item);
258: }
259: return "\n" . $ul->render(0);
260: }
261: }
262:
263:
264: 265: 266: 267:
268: public function renderBody()
269: {
270: $s = $remains = '';
271:
272: $defaultContainer = $this->getWrapper('group container');
273: $translator = $this->form->getTranslator();
274:
275: foreach ($this->form->getGroups() as $group) {
276: if (!$group->getControls() || !$group->getOption('visual')) {
277: continue;
278: }
279:
280: $container = $group->getOption('container', $defaultContainer);
281: $container = $container instanceof Html ? clone $container : Html::el($container);
282:
283: $s .= "\n" . $container->startTag();
284:
285: $text = $group->getOption('label');
286: if ($text instanceof Html) {
287: $s .= $text;
288:
289: } elseif (is_string($text)) {
290: if ($translator !== NULL) {
291: $text = $translator->translate($text);
292: }
293: $s .= "\n" . $this->getWrapper('group label')->setText($text) . "\n";
294: }
295:
296: $text = $group->getOption('description');
297: if ($text instanceof Html) {
298: $s .= $text;
299:
300: } elseif (is_string($text)) {
301: if ($translator !== NULL) {
302: $text = $translator->translate($text);
303: }
304: $s .= $this->getWrapper('group description')->setText($text) . "\n";
305: }
306:
307: $s .= $this->renderControls($group);
308:
309: $remains = $container->endTag() . "\n" . $remains;
310: if (!$group->getOption('embedNext')) {
311: $s .= $remains;
312: $remains = '';
313: }
314: }
315:
316: $s .= $remains . $this->renderControls($this->form);
317:
318: $container = $this->getWrapper('form container');
319: $container->setHtml($s);
320: return $container->render(0);
321: }
322:
323:
324: 325: 326: 327: 328:
329: public function renderControls($parent)
330: {
331: if (!($parent instanceof Nette\Forms\Container || $parent instanceof Nette\Forms\ControlGroup)) {
332: throw new Nette\InvalidArgumentException('Argument must be FormContainer or FormGroup instance.');
333: }
334:
335: $container = $this->getWrapper('controls container');
336:
337: $buttons = NULL;
338: foreach ($parent->getControls() as $control) {
339: if ($control->getOption('rendered') || $control instanceof Nette\Forms\Controls\HiddenField || $control->getForm(FALSE) !== $this->form) {
340:
341:
342: } elseif ($control instanceof Nette\Forms\Controls\Button) {
343: $buttons[] = $control;
344:
345: } else {
346: if ($buttons) {
347: $container->add($this->renderPairMulti($buttons));
348: $buttons = NULL;
349: }
350: $container->add($this->renderPair($control));
351: }
352: }
353:
354: if ($buttons) {
355: $container->add($this->renderPairMulti($buttons));
356: }
357:
358: $s = '';
359: if (count($container)) {
360: $s .= "\n" . $container . "\n";
361: }
362:
363: return $s;
364: }
365:
366:
367: 368: 369: 370:
371: public function renderPair(Nette\Forms\IControl $control)
372: {
373: $pair = $this->getWrapper('pair container');
374: $pair->add($this->renderLabel($control));
375: $pair->add($this->renderControl($control));
376: $pair->class($this->getValue($control->isRequired() ? 'pair .required' : 'pair .optional'), TRUE);
377: $pair->class($control->getOption('class'), TRUE);
378: if (++$this->counter % 2) {
379: $pair->class($this->getValue('pair .odd'), TRUE);
380: }
381: $pair->id = $control->getOption('id');
382: return $pair->render(0);
383: }
384:
385:
386: 387: 388: 389: 390:
391: public function renderPairMulti(array $controls)
392: {
393: $s = array();
394: foreach ($controls as $control) {
395: if (!$control instanceof Nette\Forms\IControl) {
396: throw new Nette\InvalidArgumentException('Argument must be array of IFormControl instances.');
397: }
398: $s[] = (string) $control->getControl();
399: }
400: $pair = $this->getWrapper('pair container');
401: $pair->add($this->renderLabel($control));
402: $pair->add($this->getWrapper('control container')->setHtml(implode(' ', $s)));
403: return $pair->render(0);
404: }
405:
406:
407: 408: 409: 410:
411: public function renderLabel(Nette\Forms\IControl $control)
412: {
413: $head = $this->getWrapper('label container');
414:
415: if ($control instanceof Nette\Forms\Controls\Checkbox || $control instanceof Nette\Forms\Controls\Button) {
416: return $head->setHtml(($head->getName() === 'td' || $head->getName() === 'th') ? ' ' : '');
417:
418: } else {
419: $label = $control->getLabel();
420: $suffix = $this->getValue('label suffix') . ($control->isRequired() ? $this->getValue('label requiredsuffix') : '');
421: if ($label instanceof Html) {
422: $label->setHtml($label->getHtml() . $suffix);
423: $suffix = '';
424: }
425: return $head->setHtml((string) $label . $suffix);
426: }
427: }
428:
429:
430: 431: 432: 433:
434: public function renderControl(Nette\Forms\IControl $control)
435: {
436: $body = $this->getWrapper('control container');
437: if ($this->counter % 2) {
438: $body->class($this->getValue('control .odd'), TRUE);
439: }
440:
441: $description = $control->getOption('description');
442: if ($description instanceof Html) {
443: $description = ' ' . $control->getOption('description');
444:
445: } elseif (is_string($description)) {
446: $description = ' ' . $this->getWrapper('control description')->setText($control->translate($description));
447:
448: } else {
449: $description = '';
450: }
451:
452: if ($control->isRequired()) {
453: $description = $this->getValue('control requiredsuffix') . $description;
454: }
455:
456: if ($this->getValue('control errors')) {
457: $description .= $this->renderErrors($control);
458: }
459:
460: if ($control instanceof Nette\Forms\Controls\Checkbox || $control instanceof Nette\Forms\Controls\Button) {
461: return $body->setHtml((string) $control->getControl() . (string) $control->getLabel() . $description);
462:
463: } else {
464: return $body->setHtml((string) $control->getControl() . $description);
465: }
466: }
467:
468:
469: 470: 471: 472:
473: protected function getWrapper($name)
474: {
475: $data = $this->getValue($name);
476: return $data instanceof Html ? clone $data : Html::el($data);
477: }
478:
479:
480: 481: 482: 483:
484: protected function getValue($name)
485: {
486: $name = explode(' ', $name);
487: $data = & $this->wrappers[$name[0]][$name[1]];
488: return $data;
489: }
490:
491: }
492: