protected function generateFields()
	{
		$hiddenFields = '';

		$fields = array_reduce( $this->fields, function( $carry, $formField ) use ( &$hiddenFields ) {

			$stringLegend    = '<legend class="screen-reader-text"><span>%s</span></legend>';
			$stringFieldset  = '<fieldset%s>%s%s</fieldset>';
			$stringRendered  = '<tr class="glsr-field %s"><th scope="row">%s</th><td>%s</td></tr>';
			$outsideRendered = '</tbody></table>%s<table class="form-table"><tbody>';

			// set field value only when rendering because we need to check the default setting
			// against the database
			$field = $formField->setValue()->getField();

			$multi    = $field->multi === true;
			$label    = $field->generateLabel();
			$rendered = $field->render();

			// render hidden inputs outside the table
			if( $field->args['type'] === 'hidden' ) {
				$hiddenFields .= $rendered;
				return $carry;
			}

			$hiddenClass = $this->isFieldHidden( $formField ) ? 'hidden' : '';

			if( $multi ) {
				if( $depends = $formField->getDataDepends() ) {
					$depends = sprintf( ' data-depends=\'%s\'', json_encode( $depends ));
				}

				$legend = $label ? sprintf( $stringLegend, $label ) : '';
				$rendered = sprintf( $stringFieldset, $depends, $legend, $rendered );
			}

			$renderedField = $field->outside
				? sprintf( $outsideRendered, $rendered )
				: sprintf( $stringRendered, $hiddenClass, $label, $rendered );

			return $carry . $renderedField;
		});

		return sprintf( '<table class="form-table"><tbody>%s</tbody></table>%s', $fields, $hiddenFields );
	}