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