1<?php
2/**
3 * Joomla! Content Management System
4 *
5 * @copyright  Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved.
6 * @license    GNU General Public License version 2 or later; see LICENSE.txt
7 */
8
9namespace Joomla\CMS\Form;
10
11defined('JPATH_PLATFORM') or die;
12
13use Joomla\CMS\Layout\FileLayout;
14use Joomla\String\Normalise;
15use Joomla\String\StringHelper;
16
17/**
18 * Abstract Form Field class for the Joomla Platform.
19 *
20 * @since  1.7.0
21 */
22abstract class FormField
23{
24	/**
25	 * The description text for the form field. Usually used in tooltips.
26	 *
27	 * @var    string
28	 * @since  1.7.0
29	 */
30	protected $description;
31
32	/**
33	 * The hint text for the form field used to display hint inside the field.
34	 *
35	 * @var    string
36	 * @since  3.2
37	 */
38	protected $hint;
39
40	/**
41	 * The autocomplete state for the form field.  If 'off' element will not be automatically
42	 * completed by browser.
43	 *
44	 * @var    mixed
45	 * @since  3.2
46	 */
47	protected $autocomplete = 'on';
48
49	/**
50	 * The spellcheck state for the form field.
51	 *
52	 * @var    boolean
53	 * @since  3.2
54	 */
55	protected $spellcheck = true;
56
57	/**
58	 * The autofocus request for the form field.  If true element will be automatically
59	 * focused on document load.
60	 *
61	 * @var    boolean
62	 * @since  3.2
63	 */
64	protected $autofocus = false;
65
66	/**
67	 * The SimpleXMLElement object of the `<field>` XML element that describes the form field.
68	 *
69	 * @var    \SimpleXMLElement
70	 * @since  1.7.0
71	 */
72	protected $element;
73
74	/**
75	 * The Form object of the form attached to the form field.
76	 *
77	 * @var    Form
78	 * @since  1.7.0
79	 */
80	protected $form;
81
82	/**
83	 * The form control prefix for field names from the JForm object attached to the form field.
84	 *
85	 * @var    string
86	 * @since  1.7.0
87	 */
88	protected $formControl;
89
90	/**
91	 * The hidden state for the form field.
92	 *
93	 * @var    boolean
94	 * @since  1.7.0
95	 */
96	protected $hidden = false;
97
98	/**
99	 * True to translate the field label string.
100	 *
101	 * @var    boolean
102	 * @since  1.7.0
103	 */
104	protected $translateLabel = true;
105
106	/**
107	 * True to translate the field description string.
108	 *
109	 * @var    boolean
110	 * @since  1.7.0
111	 */
112	protected $translateDescription = true;
113
114	/**
115	 * True to translate the field hint string.
116	 *
117	 * @var    boolean
118	 * @since  3.2
119	 */
120	protected $translateHint = true;
121
122	/**
123	 * The document id for the form field.
124	 *
125	 * @var    string
126	 * @since  1.7.0
127	 */
128	protected $id;
129
130	/**
131	 * The input for the form field.
132	 *
133	 * @var    string
134	 * @since  1.7.0
135	 */
136	protected $input;
137
138	/**
139	 * The label for the form field.
140	 *
141	 * @var    string
142	 * @since  1.7.0
143	 */
144	protected $label;
145
146	/**
147	 * The multiple state for the form field.  If true then multiple values are allowed for the
148	 * field.  Most often used for list field types.
149	 *
150	 * @var    boolean
151	 * @since  1.7.0
152	 */
153	protected $multiple = false;
154
155	/**
156	 * Allows extensions to create repeat elements
157	 *
158	 * @var    mixed
159	 * @since  3.2
160	 */
161	public $repeat = false;
162
163	/**
164	 * The pattern (Reg Ex) of value of the form field.
165	 *
166	 * @var    string
167	 * @since  1.7.0
168	 */
169	protected $pattern;
170
171	/**
172	 * The validation text of invalid value of the form field.
173	 *
174	 * @var    string
175	 * @since  4.0
176	 */
177	protected $validationtext;
178
179	/**
180	 * The name of the form field.
181	 *
182	 * @var    string
183	 * @since  1.7.0
184	 */
185	protected $name;
186
187	/**
188	 * The name of the field.
189	 *
190	 * @var    string
191	 * @since  1.7.0
192	 */
193	protected $fieldname;
194
195	/**
196	 * The group of the field.
197	 *
198	 * @var    string
199	 * @since  1.7.0
200	 */
201	protected $group;
202
203	/**
204	 * The required state for the form field.  If true then there must be a value for the field to
205	 * be considered valid.
206	 *
207	 * @var    boolean
208	 * @since  1.7.0
209	 */
210	protected $required = false;
211
212	/**
213	 * The disabled state for the form field.  If true then the field will be disabled and user can't
214	 * interact with the field.
215	 *
216	 * @var    boolean
217	 * @since  3.2
218	 */
219	protected $disabled = false;
220
221	/**
222	 * The readonly state for the form field.  If true then the field will be readonly.
223	 *
224	 * @var    boolean
225	 * @since  3.2
226	 */
227	protected $readonly = false;
228
229	/**
230	 * The form field type.
231	 *
232	 * @var    string
233	 * @since  1.7.0
234	 */
235	protected $type;
236
237	/**
238	 * The validation method for the form field.  This value will determine which method is used
239	 * to validate the value for a field.
240	 *
241	 * @var    string
242	 * @since  1.7.0
243	 */
244	protected $validate;
245
246	/**
247	 * The value of the form field.
248	 *
249	 * @var    mixed
250	 * @since  1.7.0
251	 */
252	protected $value;
253
254	/**
255	 * The default value of the form field.
256	 *
257	 * @var    mixed
258	 * @since  1.7.0
259	 */
260	protected $default;
261
262	/**
263	 * The size of the form field.
264	 *
265	 * @var    integer
266	 * @since  3.2
267	 */
268	protected $size;
269
270	/**
271	 * The class of the form field
272	 *
273	 * @var    mixed
274	 * @since  3.2
275	 */
276	protected $class;
277
278	/**
279	 * The label's CSS class of the form field
280	 *
281	 * @var    mixed
282	 * @since  1.7.0
283	 */
284	protected $labelclass;
285
286	/**
287	 * The javascript onchange of the form field.
288	 *
289	 * @var    string
290	 * @since  3.2
291	 */
292	protected $onchange;
293
294	/**
295	 * The javascript onclick of the form field.
296	 *
297	 * @var    string
298	 * @since  3.2
299	 */
300	protected $onclick;
301
302	/**
303	 * The conditions to show/hide the field.
304	 *
305	 * @var    string
306	 * @since  3.7.0
307	 */
308	protected $showon;
309
310	/**
311	 * The count value for generated name field
312	 *
313	 * @var    integer
314	 * @since  1.7.0
315	 */
316	protected static $count = 0;
317
318	/**
319	 * The string used for generated fields names
320	 *
321	 * @var    string
322	 * @since  1.7.0
323	 */
324	protected static $generated_fieldname = '__field';
325
326	/**
327	 * Name of the layout being used to render the field
328	 *
329	 * @var    string
330	 * @since  3.5
331	 */
332	protected $layout;
333
334	/**
335	 * Layout to render the form field
336	 *
337	 * @var  string
338	 */
339	protected $renderLayout = 'joomla.form.renderfield';
340
341	/**
342	 * Layout to render the label
343	 *
344	 * @var  string
345	 */
346	protected $renderLabelLayout = 'joomla.form.renderlabel';
347
348	/**
349	 * Method to instantiate the form field object.
350	 *
351	 * @param   Form  $form  The form to attach to the form field object.
352	 *
353	 * @since   1.7.0
354	 */
355	public function __construct($form = null)
356	{
357		// If there is a form passed into the constructor set the form and form control properties.
358		if ($form instanceof Form)
359		{
360			$this->form = $form;
361			$this->formControl = $form->getFormControl();
362		}
363
364		// Detect the field type if not set
365		if (!isset($this->type))
366		{
367			$parts = Normalise::fromCamelCase(get_called_class(), true);
368
369			if ($parts[0] == 'J')
370			{
371				$this->type = StringHelper::ucfirst($parts[count($parts) - 1], '_');
372			}
373			else
374			{
375				$this->type = StringHelper::ucfirst($parts[0], '_') . StringHelper::ucfirst($parts[count($parts) - 1], '_');
376			}
377		}
378	}
379
380	/**
381	 * Method to get certain otherwise inaccessible properties from the form field object.
382	 *
383	 * @param   string  $name  The property name for which to get the value.
384	 *
385	 * @return  mixed  The property value or null.
386	 *
387	 * @since   1.7.0
388	 */
389	public function __get($name)
390	{
391		switch ($name)
392		{
393			case 'description':
394			case 'hint':
395			case 'formControl':
396			case 'hidden':
397			case 'id':
398			case 'multiple':
399			case 'name':
400			case 'required':
401			case 'type':
402			case 'validate':
403			case 'value':
404			case 'class':
405			case 'layout':
406			case 'labelclass':
407			case 'size':
408			case 'onchange':
409			case 'onclick':
410			case 'fieldname':
411			case 'group':
412			case 'disabled':
413			case 'readonly':
414			case 'autofocus':
415			case 'autocomplete':
416			case 'spellcheck':
417			case 'validationtext':
418			case 'showon':
419				return $this->$name;
420
421			case 'input':
422				// If the input hasn't yet been generated, generate it.
423				if (empty($this->input))
424				{
425					$this->input = $this->getInput();
426				}
427
428				return $this->input;
429
430			case 'label':
431				// If the label hasn't yet been generated, generate it.
432				if (empty($this->label))
433				{
434					$this->label = $this->getLabel();
435				}
436
437				return $this->label;
438
439			case 'title':
440				return $this->getTitle();
441		}
442
443		return;
444	}
445
446	/**
447	 * Method to set certain otherwise inaccessible properties of the form field object.
448	 *
449	 * @param   string  $name   The property name for which to set the value.
450	 * @param   mixed   $value  The value of the property.
451	 *
452	 * @return  void
453	 *
454	 * @since   3.2
455	 */
456	public function __set($name, $value)
457	{
458		switch ($name)
459		{
460			case 'class':
461				// Removes spaces from left & right and extra spaces from middle
462				$value = preg_replace('/\s+/', ' ', trim((string) $value));
463
464			case 'description':
465			case 'hint':
466			case 'value':
467			case 'labelclass':
468			case 'layout':
469			case 'onchange':
470			case 'onclick':
471			case 'validate':
472			case 'pattern':
473			case 'validationtext':
474			case 'group':
475			case 'showon':
476			case 'default':
477				$this->$name = (string) $value;
478				break;
479
480			case 'id':
481				$this->id = $this->getId((string) $value, $this->fieldname);
482				break;
483
484			case 'fieldname':
485				$this->fieldname = $this->getFieldName((string) $value);
486				break;
487
488			case 'name':
489				$this->fieldname = $this->getFieldName((string) $value);
490				$this->name = $this->getName($this->fieldname);
491				break;
492
493			case 'multiple':
494				// Allow for field classes to force the multiple values option.
495				$value = (string) $value;
496				$value = $value === '' && isset($this->forceMultiple) ? (string) $this->forceMultiple : $value;
497
498			case 'required':
499			case 'disabled':
500			case 'readonly':
501			case 'autofocus':
502			case 'hidden':
503				$value = (string) $value;
504				$this->$name = ($value === 'true' || $value === $name || $value === '1');
505				break;
506
507			case 'autocomplete':
508				$value = (string) $value;
509				$value = ($value == 'on' || $value == '') ? 'on' : $value;
510				$this->$name = ($value === 'false' || $value === 'off' || $value === '0') ? false : $value;
511				break;
512
513			case 'spellcheck':
514			case 'translateLabel':
515			case 'translateDescription':
516			case 'translateHint':
517				$value = (string) $value;
518				$this->$name = !($value === 'false' || $value === 'off' || $value === '0');
519				break;
520
521			case 'translate_label':
522				$value = (string) $value;
523				$this->translateLabel = $this->translateLabel && !($value === 'false' || $value === 'off' || $value === '0');
524				break;
525
526			case 'translate_description':
527				$value = (string) $value;
528				$this->translateDescription = $this->translateDescription && !($value === 'false' || $value === 'off' || $value === '0');
529				break;
530
531			case 'size':
532				$this->$name = (int) $value;
533				break;
534
535			default:
536				if (property_exists(__CLASS__, $name))
537				{
538					\JLog::add("Cannot access protected / private property $name of " . __CLASS__);
539				}
540				else
541				{
542					$this->$name = $value;
543				}
544		}
545	}
546
547	/**
548	 * Method to attach a JForm object to the field.
549	 *
550	 * @param   Form  $form  The JForm object to attach to the form field.
551	 *
552	 * @return  FormField  The form field object so that the method can be used in a chain.
553	 *
554	 * @since   1.7.0
555	 */
556	public function setForm(Form $form)
557	{
558		$this->form = $form;
559		$this->formControl = $form->getFormControl();
560
561		return $this;
562	}
563
564	/**
565	 * Method to attach a JForm object to the field.
566	 *
567	 * @param   \SimpleXMLElement  $element  The SimpleXMLElement object representing the `<field>` tag for the form field object.
568	 * @param   mixed              $value    The form field value to validate.
569	 * @param   string             $group    The field name group control value. This acts as as an array container for the field.
570	 *                                       For example if the field has name="foo" and the group value is set to "bar" then the
571	 *                                       full field name would end up being "bar[foo]".
572	 *
573	 * @return  boolean  True on success.
574	 *
575	 * @since   1.7.0
576	 */
577	public function setup(\SimpleXMLElement $element, $value, $group = null)
578	{
579		// Make sure there is a valid JFormField XML element.
580		if ((string) $element->getName() != 'field')
581		{
582			return false;
583		}
584
585		// Reset the input and label values.
586		$this->input = null;
587		$this->label = null;
588
589		// Set the XML element object.
590		$this->element = $element;
591
592		// Set the group of the field.
593		$this->group = $group;
594
595		$attributes = array(
596			'multiple', 'name', 'id', 'hint', 'class', 'description', 'labelclass', 'onchange', 'onclick', 'validate', 'pattern', 'validationtext', 'default',
597			'required', 'disabled', 'readonly', 'autofocus', 'hidden', 'autocomplete', 'spellcheck', 'translateHint', 'translateLabel',
598			'translate_label', 'translateDescription', 'translate_description', 'size', 'showon');
599
600		$this->default = isset($element['value']) ? (string) $element['value'] : $this->default;
601
602		// Set the field default value.
603		if ($element['multiple'] && is_string($value) && is_array(json_decode($value, true)))
604		{
605			$this->value = (array) json_decode($value);
606		}
607		else
608		{
609			$this->value = $value;
610		}
611
612		foreach ($attributes as $attributeName)
613		{
614			$this->__set($attributeName, $element[$attributeName]);
615		}
616
617		// Allow for repeatable elements
618		$repeat = (string) $element['repeat'];
619		$this->repeat = ($repeat == 'true' || $repeat == 'multiple' || (!empty($this->form->repeat) && $this->form->repeat == 1));
620
621		// Set the visibility.
622		$this->hidden = ($this->hidden || (string) $element['type'] == 'hidden');
623
624		$this->layout = !empty($this->element['layout']) ? (string) $this->element['layout'] : $this->layout;
625
626		// Add required to class list if field is required.
627		if ($this->required)
628		{
629			$this->class = trim($this->class . ' required');
630		}
631
632		return true;
633	}
634
635	/**
636	 * Simple method to set the value
637	 *
638	 * @param   mixed  $value  Value to set
639	 *
640	 * @return  void
641	 *
642	 * @since   3.2
643	 */
644	public function setValue($value)
645	{
646		$this->value = $value;
647	}
648
649	/**
650	 * Method to get the id used for the field input tag.
651	 *
652	 * @param   string  $fieldId    The field element id.
653	 * @param   string  $fieldName  The field element name.
654	 *
655	 * @return  string  The id to be used for the field input tag.
656	 *
657	 * @since   1.7.0
658	 */
659	protected function getId($fieldId, $fieldName)
660	{
661		$id = '';
662
663		// If there is a form control set for the attached form add it first.
664		if ($this->formControl)
665		{
666			$id .= $this->formControl;
667		}
668
669		// If the field is in a group add the group control to the field id.
670		if ($this->group)
671		{
672			// If we already have an id segment add the group control as another level.
673			if ($id)
674			{
675				$id .= '_' . str_replace('.', '_', $this->group);
676			}
677			else
678			{
679				$id .= str_replace('.', '_', $this->group);
680			}
681		}
682
683		// If we already have an id segment add the field id/name as another level.
684		if ($id)
685		{
686			$id .= '_' . ($fieldId ? $fieldId : $fieldName);
687		}
688		else
689		{
690			$id .= ($fieldId ? $fieldId : $fieldName);
691		}
692
693		// Clean up any invalid characters.
694		$id = preg_replace('#\W#', '_', $id);
695
696		// If this is a repeatable element, add the repeat count to the ID
697		if ($this->repeat)
698		{
699			$repeatCounter = empty($this->form->repeatCounter) ? 0 : $this->form->repeatCounter;
700			$id .= '-' . $repeatCounter;
701
702			if (strtolower($this->type) == 'radio')
703			{
704				$id .= '-';
705			}
706		}
707
708		return $id;
709	}
710
711	/**
712	 * Method to get the field input markup.
713	 *
714	 * @return  string  The field input markup.
715	 *
716	 * @since   1.7.0
717	 */
718	protected function getInput()
719	{
720		if (empty($this->layout))
721		{
722			throw new \UnexpectedValueException(sprintf('%s has no layout assigned.', $this->name));
723		}
724
725		return $this->getRenderer($this->layout)->render($this->getLayoutData());
726	}
727
728	/**
729	 * Method to get the field title.
730	 *
731	 * @return  string  The field title.
732	 *
733	 * @since   1.7.0
734	 */
735	protected function getTitle()
736	{
737		$title = '';
738
739		if ($this->hidden)
740		{
741			return $title;
742		}
743
744		// Get the label text from the XML element, defaulting to the element name.
745		$title = $this->element['label'] ? (string) $this->element['label'] : (string) $this->element['name'];
746		$title = $this->translateLabel ? \JText::_($title) : $title;
747
748		return $title;
749	}
750
751	/**
752	 * Method to get the field label markup.
753	 *
754	 * @return  string  The field label markup.
755	 *
756	 * @since   1.7.0
757	 */
758	protected function getLabel()
759	{
760		if ($this->hidden)
761		{
762			return '';
763		}
764
765		$data = $this->getLayoutData();
766
767		// Forcing the Alias field to display the tip below
768		$position = $this->element['name'] == 'alias' ? ' data-placement="bottom" ' : '';
769
770		// Here mainly for B/C with old layouts. This can be done in the layouts directly
771		$extraData = array(
772			'text'        => $data['label'],
773			'for'         => $this->id,
774			'classes'     => explode(' ', $data['labelclass']),
775			'position'    => $position,
776		);
777
778		return $this->getRenderer($this->renderLabelLayout)->render(array_merge($data, $extraData));
779	}
780
781	/**
782	 * Method to get the name used for the field input tag.
783	 *
784	 * @param   string  $fieldName  The field element name.
785	 *
786	 * @return  string  The name to be used for the field input tag.
787	 *
788	 * @since   1.7.0
789	 */
790	protected function getName($fieldName)
791	{
792		// To support repeated element, extensions can set this in plugin->onRenderSettings
793
794		$name = '';
795
796		// If there is a form control set for the attached form add it first.
797		if ($this->formControl)
798		{
799			$name .= $this->formControl;
800		}
801
802		// If the field is in a group add the group control to the field name.
803		if ($this->group)
804		{
805			// If we already have a name segment add the group control as another level.
806			$groups = explode('.', $this->group);
807
808			if ($name)
809			{
810				foreach ($groups as $group)
811				{
812					$name .= '[' . $group . ']';
813				}
814			}
815			else
816			{
817				$name .= array_shift($groups);
818
819				foreach ($groups as $group)
820				{
821					$name .= '[' . $group . ']';
822				}
823			}
824		}
825
826		// If we already have a name segment add the field name as another level.
827		if ($name)
828		{
829			$name .= '[' . $fieldName . ']';
830		}
831		else
832		{
833			$name .= $fieldName;
834		}
835
836		// If the field should support multiple values add the final array segment.
837		if ($this->multiple)
838		{
839			switch (strtolower((string) $this->element['type']))
840			{
841				case 'text':
842				case 'textarea':
843				case 'email':
844				case 'password':
845				case 'radio':
846				case 'calendar':
847				case 'editor':
848				case 'hidden':
849					break;
850				default:
851					$name .= '[]';
852			}
853		}
854
855		return $name;
856	}
857
858	/**
859	 * Method to get the field name used.
860	 *
861	 * @param   string  $fieldName  The field element name.
862	 *
863	 * @return  string  The field name
864	 *
865	 * @since   1.7.0
866	 */
867	protected function getFieldName($fieldName)
868	{
869		if ($fieldName)
870		{
871			return $fieldName;
872		}
873		else
874		{
875			self::$count = self::$count + 1;
876
877			return self::$generated_fieldname . self::$count;
878		}
879	}
880
881	/**
882	 * Method to get an attribute of the field
883	 *
884	 * @param   string  $name     Name of the attribute to get
885	 * @param   mixed   $default  Optional value to return if attribute not found
886	 *
887	 * @return  mixed             Value of the attribute / default
888	 *
889	 * @since   3.2
890	 */
891	public function getAttribute($name, $default = null)
892	{
893		if ($this->element instanceof \SimpleXMLElement)
894		{
895			$attributes = $this->element->attributes();
896
897			// Ensure that the attribute exists
898			if ($attributes->$name !== null)
899			{
900				return (string) $attributes->$name;
901			}
902		}
903
904		return $default;
905	}
906
907	/**
908	 * Method to get a control group with label and input.
909	 *
910	 * @return  string  A string containing the html for the control group
911	 *
912	 * @since      3.2
913	 * @deprecated 3.2.3 Use renderField() instead
914	 */
915	public function getControlGroup()
916	{
917		\JLog::add('FormField->getControlGroup() is deprecated use FormField->renderField().', \JLog::WARNING, 'deprecated');
918
919		return $this->renderField();
920	}
921
922	/**
923	 * Render a layout of this field
924	 *
925	 * @param   string  $layoutId  Layout identifier
926	 * @param   array   $data      Optional data for the layout
927	 *
928	 * @return  string
929	 *
930	 * @since   3.5
931	 */
932	public function render($layoutId, $data = array())
933	{
934		$data = array_merge($this->getLayoutData(), $data);
935
936		return $this->getRenderer($layoutId)->render($data);
937	}
938
939	/**
940	 * Method to get a control group with label and input.
941	 *
942	 * @param   array  $options  Options to be passed into the rendering of the field
943	 *
944	 * @return  string  A string containing the html for the control group
945	 *
946	 * @since   3.2
947	 */
948	public function renderField($options = array())
949	{
950		if ($this->hidden)
951		{
952			return $this->getInput();
953		}
954
955		if (!isset($options['class']))
956		{
957			$options['class'] = '';
958		}
959
960		$options['rel'] = '';
961
962		if (empty($options['hiddenLabel']) && $this->getAttribute('hiddenLabel'))
963		{
964			$options['hiddenLabel'] = true;
965		}
966
967		if ($this->showon)
968		{
969			$options['rel']           = ' data-showon=\'' .
970				json_encode(FormHelper::parseShowOnConditions($this->showon, $this->formControl, $this->group)) . '\'';
971			$options['showonEnabled'] = true;
972		}
973
974		$data = array(
975			'input'   => $this->getInput(),
976			'label'   => $this->getLabel(),
977			'options' => $options,
978		);
979
980		return $this->getRenderer($this->renderLayout)->render($data);
981	}
982
983	/**
984	 * Method to get the data to be passed to the layout for rendering.
985	 *
986	 * @return  array
987	 *
988	 * @since 3.5
989	 */
990	protected function getLayoutData()
991	{
992		// Label preprocess
993		$label = $this->element['label'] ? (string) $this->element['label'] : (string) $this->element['name'];
994		$label = $this->translateLabel ? \JText::_($label) : $label;
995
996		// Description preprocess
997		$description = !empty($this->description) ? $this->description : null;
998		$description = !empty($description) && $this->translateDescription ? \JText::_($description) : $description;
999
1000		$alt = preg_replace('/[^a-zA-Z0-9_\-]/', '_', $this->fieldname);
1001
1002		return array(
1003			'autocomplete'   => $this->autocomplete,
1004			'autofocus'      => $this->autofocus,
1005			'class'          => $this->class,
1006			'description'    => $description,
1007			'disabled'       => $this->disabled,
1008			'field'          => $this,
1009			'group'          => $this->group,
1010			'hidden'         => $this->hidden,
1011			'hint'           => $this->translateHint ? \JText::alt($this->hint, $alt) : $this->hint,
1012			'id'             => $this->id,
1013			'label'          => $label,
1014			'labelclass'     => $this->labelclass,
1015			'multiple'       => $this->multiple,
1016			'name'           => $this->name,
1017			'onchange'       => $this->onchange,
1018			'onclick'        => $this->onclick,
1019			'pattern'        => $this->pattern,
1020			'validationtext' => $this->validationtext,
1021			'readonly'       => $this->readonly,
1022			'repeat'         => $this->repeat,
1023			'required'       => (bool) $this->required,
1024			'size'           => $this->size,
1025			'spellcheck'     => $this->spellcheck,
1026			'validate'       => $this->validate,
1027			'value'          => $this->value,
1028		);
1029	}
1030
1031	/**
1032	 * Allow to override renderer include paths in child fields
1033	 *
1034	 * @return  array
1035	 *
1036	 * @since   3.5
1037	 */
1038	protected function getLayoutPaths()
1039	{
1040		$renderer = new FileLayout('default');
1041
1042		return $renderer->getDefaultIncludePaths();
1043	}
1044
1045	/**
1046	 * Get the renderer
1047	 *
1048	 * @param   string  $layoutId  Id to load
1049	 *
1050	 * @return  FileLayout
1051	 *
1052	 * @since   3.5
1053	 */
1054	protected function getRenderer($layoutId = 'default')
1055	{
1056		$renderer = new FileLayout($layoutId);
1057
1058		$renderer->setDebug($this->isDebugEnabled());
1059
1060		$layoutPaths = $this->getLayoutPaths();
1061
1062		if ($layoutPaths)
1063		{
1064			$renderer->setIncludePaths($layoutPaths);
1065		}
1066
1067		return $renderer;
1068	}
1069
1070	/**
1071	 * Is debug enabled for this field
1072	 *
1073	 * @return  boolean
1074	 *
1075	 * @since   3.5
1076	 */
1077	protected function isDebugEnabled()
1078	{
1079		return $this->getAttribute('debug', 'false') === 'true';
1080	}
1081}
1082