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