'use strict';

function applyFormMessageAttributes(component, formData) {
	var messages = formData && formData.messages ? formData.messages : {};
	var attributes = component && component.getAttributes ? component.getAttributes() : {};
	var next = {};

	addDefaultAttribute(next, attributes, 'data-subin-form-title', formData && formData.title ? formData.title : '');
	addDefaultAttribute(next, attributes, 'data-subin-form-description', formData && formData.description ? formData.description : '');
	addDefaultAttribute(next, attributes, 'data-subin-validation-error-message', messages.validation_error || 'Please check the highlighted fields and try again.');
	addDefaultAttribute(next, attributes, 'data-subin-required-error-message', messages.required_field_error || 'This field is required.');
	addDefaultAttribute(next, attributes, 'data-subin-spam-error-message', messages.spam_protection_error || 'Your submission could not be accepted.');
	addDefaultAttribute(next, attributes, 'data-subin-success-message', messages.success_message || 'Thanks, your message has been sent.');

	if (Object.keys(next).length && component && component.addAttributes) {
		component.addAttributes(next);
	}
}

function addDefaultAttribute(next, attributes, key, value) {
	if (!attributes[key] && value) {
		next[key] = value;
	}
}

function configureFormWrapperTraits(component) {
	if (!component || !component.set) {
		return;
	}

	component.set('traits', withBackgroundMediaTrait([
		textTrait('data-subin-form-title', 'Form title'),
		contentTextTrait('data-subin-form-description', 'Form description'),
		contentTextTrait('data-subin-validation-error-message', 'Validation error message'),
		textTrait('data-subin-required-error-message', 'Default required message'),
		contentTextTrait('data-subin-spam-error-message', 'Spam protection message'),
		contentTextTrait('data-subin-success-message', 'Success message'),
	]));
}

function subinComponentTypeFromName(subinType) {
	var map = {
		'text-block': 'subin-text-block',
		'html-block': 'subin-html-block',
		notice: 'subin-notice-block',
		divider: 'subin-divider-block',
		privacy: 'subin-privacy-block',
		section: 'subin-section-block',
		spacer: 'subin-spacer-block',
		'image-block': 'subin-image-block',
	};

	return map[subinType] || '';
}

function refreshStyleManager(editor) {
	var styleManager = editor && editor.StyleManager;

	if (styleManager && styleManager.render) {
		window.setTimeout(function () {
			styleManager.render();
			organizeUnifiedInspector(editor);
		}, 0);
	}
}

function walkComponents(components, callback) {
	if (!components || !components.forEach) {
		return;
	}

	components.forEach(function (component) {
		callback(component);
		walkComponents(component.components && component.components(), callback);
	});
}

function configureComponentStylable(component, fieldMap, formData) {
	var attributes = component.getAttributes ? component.getAttributes() : {};
	var type = component.get ? component.get('type') : '';
	var subinType = attributes['data-subin-component'] || subinComponentNameFromType(type);

	if (subinType === 'form-wrapper') {
		addClasses(component, ['subin-gjs-form-preview', 'subin-gjs-form-flow']);
		applyFormMessageAttributes(component, formData || {});
		configureBackgroundMediaComponent(component);
		configureFormWrapperTraits(component);
		component.set('stylable', subinWrapperStyleProps());
		component.set('droppable', true);
		component.set('name', 'Form');
	} else if (subinType === 'row') {
		addClasses(component, ['subin-gjs-row']);
		ensureRowId(component);
		ensureUniqueStyleTarget(component, 'row');
		component.set('name', 'Row');
		component.set('droppable', function (target) {
			return isFlowLayoutComponent(target);
		});
		component.set('stylable', subinRowStyleProps());
		component.set('traits', withBackgroundMediaTrait([
			{
				type: 'subin-row-actions',
				name: 'subin-row-actions',
				label: 'Row actions',
			},
		]));
		configureBackgroundMediaComponent(component);
	} else if (subinType === 'field') {
		applyFieldDataAttributes(component, fieldMap || {});
		updateFieldLayerName(component);
		component.set('stylable', subinFieldStyleProps());
		configureFlowLayoutComponent(component);
		configureFieldComponentTraits(component);
	} else if (subinType === 'submit-button') {
		component.set('name', 'Submit button');
		component.set('stylable', subinButtonStyleProps());
		configureFlowLayoutComponent(component);
		component.set('traits', [
			{
				type: 'subin-text',
				name: 'data-subin-button-text',
				label: 'Button text',
			},
		]);
		if (!attributes['data-subin-button-text']) {
			component.addAttributes({
				'data-subin-button-text': componentPlainText(component) || 'Submit',
			});
		}
	} else if (subinType) {
		component.set('name', humanReadableComponentLabel(component, subinType));
		component.set('stylable', subinContentStyleProps(subinType));
		configureFlowLayoutComponent(component);
		configureContentComponentTraits(component, subinType);
		configureBackgroundMediaComponent(component);
	}
}

function buildEditorFieldMap(formData) {
	var map = {};
	var fields = formData && Array.isArray(formData.fields) ? formData.fields : [];

	fields.forEach(function (field) {
		if (field.id) {
			map['id:' + String(field.id)] = field;
		}
		if (field.key) {
			map['key:' + field.key] = field;
		}
	});

	return map;
}

function applyFieldDataAttributes(component, fieldMap) {
	var attributes = component.getAttributes ? component.getAttributes() : {};
	var field = attributes['data-subin-field-id'] ? fieldMap['id:' + attributes['data-subin-field-id']] : null;

	if (!field && attributes['data-subin-field-key']) {
		field = fieldMap['key:' + attributes['data-subin-field-key']];
	}

	if (!field) {
		return;
	}

	var next = {};
	if (!attributes['data-subin-label'] && !attributes['data-subin-field-label']) {
		next['data-subin-label'] = field.label || field.key || defaultFieldLabel(field.type || 'text');
	}
	if (!attributes['data-subin-placeholder']) {
		next['data-subin-placeholder'] = field.placeholder || '';
	}
	if (attributes['data-subin-required'] === undefined) {
		next['data-subin-required'] = field.required ? '1' : '0';
	}
	if (isChoiceField(field.type) && !hasValidChoiceOptions(attributes['data-subin-options'])) {
		next['data-subin-options'] = stringifyChoiceOptions(choiceOptionsFromField(field));
	}

	if (Object.keys(next).length) {
		component.addAttributes(next);
	}
}

function subinComponentNameFromType(type) {
	var map = {
		'subin-row': 'row',
		'subin-field': 'field',
		'subin-submit-button': 'submit-button',
		'subin-text-block': 'text-block',
		'subin-html-block': 'html-block',
		'subin-notice-block': 'notice',
		'subin-divider-block': 'divider',
		'subin-privacy-block': 'privacy',
		'subin-section-block': 'section',
		'subin-spacer-block': 'spacer',
		'subin-image-block': 'image-block',
		wrapper: 'form-wrapper',
	};

	return map[type] || '';
}

function normalizeSubmissionInboxRowsInProject(project) {
	var normalized;

	try {
		normalized = JSON.parse(JSON.stringify(project || {}));
	} catch (error) {
		return project;
	}

	if (Array.isArray(normalized.components)) {
		normalized.components = normalizeComponentListRows(normalized.components);
	}

	if (Array.isArray(normalized.pages)) {
		normalized.pages.forEach(function (page) {
			(page.frames || []).forEach(function (frame) {
				if (frame && frame.component) {
					frame.component = normalizeComponentRows(frame.component);
				}
			});
		});
	}

	return normalized;
}

function normalizeComponentListRows(components) {
	return (components || []).map(function (component) {
		return normalizeComponentRows(component);
	});
}

function normalizeComponentRows(component) {
	var subinType;

	if (!component || typeof component !== 'object') {
		return component;
	}

	subinType = plainComponentSubinType(component);

	if (Array.isArray(component.components)) {
		component.components = normalizeComponentListRows(component.components);
	}

	if (subinType === 'form-wrapper') {
		component.components = normalizeWrapperChildrenIntoRows(component.components || []);
	}

	if (subinType === 'row') {
		component.attributes = Object.assign({}, component.attributes || {}, {
			'data-subin-component': 'row',
			'data-subin-row-id': component.attributes && component.attributes['data-subin-row-id'] ? component.attributes['data-subin-row-id'] : 'row-' + Date.now() + '-' + Math.floor(Math.random() * 100000),
		});
		component.type = component.type || 'subin-row';
	}

	return component;
}

function normalizeWrapperChildrenIntoRows(children) {
	var rows = [];

	(children || []).forEach(function (child) {
		var normalized = normalizeComponentRows(child);
		var subinType = plainComponentSubinType(normalized);

		if (subinType === 'row') {
			rows.push(normalized);
		} else if (isPlainFlowComponent(normalized)) {
			rows.push(subinRowComponent([normalized]));
		} else {
			rows.push(normalized);
		}
	});

	return rows;
}

function plainComponentSubinType(component) {
	var attributes = component && component.attributes ? component.attributes : {};
	return attributes['data-subin-component'] || subinComponentNameFromType(component && component.type ? component.type : '');
}

function isPlainFlowComponent(component) {
	return [
		'field',
		'submit-button',
		'text-block',
		'html-block',
		'notice',
		'divider',
		'privacy',
		'section',
		'spacer',
		'image-block',
	].indexOf(plainComponentSubinType(component)) !== -1;
}

function readSubmissionInboxFormData() {
	var dataScript = document.getElementById('subin-grapesjs-form-data');

	if (!dataScript) {
		return null;
	}

	try {
		return JSON.parse(dataScript.textContent || '{}');
	} catch (error) {
		return null;
	}
}

function buildSubmissionInboxFormComponents(formData) {
	var components = [];
	var fields = Array.isArray(formData.fields) ? formData.fields : [];
	var designStyles = formData.design_styles || {};

	if (formData.title) {
		components.push(subinContentComponent('subin-text-block', 'text-block', '<h2 class="subin-form-title">' + escapeHtml(formData.title) + '</h2>', 'form-title', styleFromDesignSettings(designStyles.header_title, 'header_title')));
	}

	if (formData.description) {
		components.push(subinContentComponent('subin-text-block', 'text-block', '<p class="subin-form-description">' + escapeHtml(formData.description) + '</p>', 'form-description', styleFromDesignSettings(designStyles.header_description, 'header_description')));
	}

	if (fields.length) {
		fields.forEach(function (field) {
			components.push(subinExistingFieldComponent(field, formData));
		});
	} else {
		components.push(subinContentComponent('subin-notice-block', 'notice', '<p>No fields have been added to this form yet.</p>', 'empty-fields', styleFromDesignSettings(designStyles.design_block, 'design_block')));
	}

	components.push(subinSubmitButtonComponent(formData.submit_button_text || 'Submit', 'submit-button', styleFromDesignSettings(designStyles.submit_button, 'submit_button')));

	if (formData.privacy_text) {
		components.push(subinContentComponent('subin-privacy-block', 'privacy', '<p>' + escapeHtml(formData.privacy_text) + '</p>', 'privacy-text', styleFromDesignSettings(designStyles.privacy_text, 'privacy_text')));
	}

	if (formData.footer_note) {
		components.push(subinContentComponent('subin-text-block', 'text-block', '<p>' + escapeHtml(formData.footer_note) + '</p>', 'footer-note', styleFromDesignSettings(designStyles.footer, 'footer')));
	}

	return components.map(function (component) {
		return subinRowComponent([component]);
	});
}

function applyFormWrapperStyle(editor, formData) {
	var wrapperStyle = styleFromDesignSettings(formData.design_styles && formData.design_styles.form_wrapper, 'form_wrapper');

	if (Object.keys(wrapperStyle).length) {
		editor.getWrapper().setStyle(wrapperStyle);
	}
}

function subinFieldBlock(id, label, fieldType, fieldKey, fieldLabel, placeholder, required, icon) {
	var attributes = {
		'data-subin-component': 'field',
		'data-subin-field-type': fieldType,
		'data-subin-field-key': fieldKey,
		'data-subin-label': fieldLabel,
		'data-subin-placeholder': placeholder || '',
		'data-subin-required': required ? '1' : '0',
	};
	var options = isChoiceField(fieldType) ? defaultChoiceOptions() : [];

	if (isChoiceField(fieldType)) {
		attributes['data-subin-options'] = stringifyChoiceOptions(options);
	}

	if (supportsHelpTextTrait(fieldType) || fieldType === 'consent') {
		attributes['data-subin-help-text'] = '';
	}

	if (fieldType === 'consent') {
		attributes['data-subin-consent-text'] = fieldLabel;
	}

	if (fieldType === 'hidden') {
		attributes['data-subin-default-value'] = '';
	}

	return {
		id: id,
		label: blockLabel(icon, label, blockDescription(id)),
		category: blockCategory('Fields', true),
		media: '',
		content: {
			type: 'subin-field',
			name: fieldTypeLabel(fieldType),
			attributes: attributes,
			components: subinFieldPreview(fieldType, fieldLabel, placeholder || '', !!required, fieldKey, options, '', fieldLabel, ''),
			resizable: subinFlowResizableOptions(),
		},
	};
}

function subinExistingFieldComponent(field, formData) {
	var fieldType = field.type || 'text';
	var fieldLabel = field.label || field.key || 'Field';
	var fieldStyleId = field.id ? 'field-' + field.id : 'field-' + (field.key || Date.now());
	var fieldStyles = formData.field_styles && field.id ? formData.field_styles[String(field.id)] || formData.field_styles[field.id] : null;
	var options = isChoiceField(fieldType) ? choiceOptionsFromField(field) : [];
	var attributes = {
		'data-subin-component': 'field',
		'data-subin-field-type': fieldType,
		'data-subin-field-key': field.key || '',
		'data-subin-style-id': fieldStyleId,
		'data-subin-label': fieldLabel,
		'data-subin-placeholder': field.placeholder || '',
		'data-subin-help-text': field.help_text || '',
	};

	if (field.id) {
		attributes['data-subin-field-id'] = String(field.id);
	}

	if (field.required) {
		attributes['data-subin-required'] = '1';
	}

	if (isChoiceField(fieldType)) {
		attributes['data-subin-options'] = stringifyChoiceOptions(options);
	}

	if (fieldType === 'consent') {
		attributes['data-subin-consent-text'] = fieldLabel;
	}

	if (fieldType === 'hidden') {
		attributes['data-subin-default-value'] = field.default_value || '';
	}

	return {
		type: 'subin-field',
		name: fieldTypeLabel(fieldType),
		classes: [styleClassName(fieldStyleId)],
		attributes: attributes,
		components: subinFieldPreview(fieldType, fieldLabel, field.placeholder || '', !!field.required, field.key || '', options, field.help_text || '', fieldLabel, field.default_value || ''),
		style: fieldRootStyle(formData.design_styles || {}, fieldStyles || {}),
		stylable: subinFieldStyleProps(),
		resizable: subinFlowResizableOptions(),
	};
}

function subinContentBlock(id, label, type, componentName, previewContent, category, icon) {
	return {
		id: id,
		label: blockLabel(icon, label, blockDescription(id)),
		category: blockCategory(category || 'Content', category !== 'Layout'),
		media: '',
		content: {
			type: type,
			attributes: {
				'data-subin-component': componentName,
				'data-subin-content': defaultContentAttribute(previewContent, componentName),
			},
			components: previewContent,
		},
	};
}

function subinSubmitButtonBlock() {
	return {
		id: 'subin-submit-button',
		label: blockLabel('Go', 'Submit button', blockDescription('subin-submit-button')),
		category: blockCategory('Form', true),
		media: '',
		content: {
			type: 'subin-submit-button',
			attributes: {
				'data-subin-component': 'submit-button',
			},
		},
	};
}

function blockCategory(label, open) {
	return {
		id: 'subin-' + String(label).toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, ''),
		label: label,
		open: open !== false,
	};
}

function blockLabel(icon, label, description) {
	var copy = '<span class="subin-gjs-block-label__copy"><strong>' + escapeHtml(label) + '</strong>';

	if (description) {
		copy += '<small>' + escapeHtml(description) + '</small>';
	}

	copy += '</span>';

	return '<span class="subin-gjs-block-label"><span class="subin-gjs-block-label__icon" aria-hidden="true">' + escapeHtml(icon || '*') + '</span>' + copy + '</span>';
}

function blockDescription(id) {
	var descriptions = {
		'subin-field-text': 'Single-line answer field.',
		'subin-field-email': 'Validated email address field.',
		'subin-field-phone': 'Phone number input field.',
		'subin-field-date': 'Date input field.',
		'subin-field-textarea': 'Long message field.',
		'subin-field-select': 'Dropdown choice field.',
		'subin-field-radio': 'Single-choice radio group.',
		'subin-field-checkbox': 'Multiple-choice checkbox group.',
		'subin-field-consent': 'Required consent checkbox.',
		'subin-field-hidden': 'Submitted hidden value.',
		'subin-text-block': 'Supporting text content.',
		'subin-html-block': 'Safe custom HTML content.',
		'subin-notice-block': 'Highlighted visitor note.',
		'subin-divider-block': 'Horizontal separator.',
		'subin-privacy-block': 'Privacy or consent note.',
		'subin-submit-button': 'Form submission button.',
		'subin-section-block': 'Grouped content area.',
		'subin-spacer-block': 'Vertical spacing block.',
		'subin-image-block': 'Image content block.',
	};

	return descriptions[id] || '';
}

function subinContentComponent(type, componentName, previewContent, styleId, componentStyle) {
	styleId = styleId || componentName + '-' + Date.now() + '-' + Math.floor(Math.random() * 100000);
	var traits = subinContentTraits(componentName);
	if (isBackgroundMediaComponentData(componentName)) {
		traits = traits.concat([backgroundMediaTrait()]);
	}

	return {
		type: type,
		classes: [styleClassName(styleId)].concat(contentBlockClasses(type, componentName)),
		attributes: {
			'data-subin-component': componentName,
			'data-subin-style-id': styleId,
			'data-subin-content': defaultContentAttribute(previewContent, componentName),
		},
		components: previewContent,
		style: componentStyle || {},
		stylable: subinContentStyleProps(componentName),
		traits: traits,
		resizable: subinFlowResizableOptions(),
	};
}

function defaultContentAttribute(previewContent, componentName) {
	if (['text-block', 'html-block', 'notice', 'privacy'].indexOf(String(componentName || '')) === -1) {
		return '';
	}

	if (componentName === 'html-block') {
		return sanitizeSafeHtml(previewContent || defaultContentText(componentName));
	}

	return String(previewContent || defaultContentText(componentName)).replace(/<[^>]*>/g, '').replace(/\s+/g, ' ').trim();
}

function subinRowComponent(children, styleId, componentStyle) {
	styleId = styleId || 'row-' + Date.now() + '-' + Math.floor(Math.random() * 100000);

	return {
		type: 'subin-row',
		name: 'Row',
		classes: [styleClassName(styleId), 'subin-gjs-row'],
		attributes: {
			'data-subin-component': 'row',
			'data-subin-row-id': styleId,
			'data-subin-style-id': styleId,
		},
		components: children || [],
		style: componentStyle || {},
		stylable: subinRowStyleProps(),
		traits: withBackgroundMediaTrait([
			{
				type: 'subin-row-actions',
				name: 'subin-row-actions',
				label: 'Row actions',
			},
		]),
		droppable: true,
	};
}

function subinSubmitButtonComponent(buttonText, styleId, componentStyle) {
	styleId = styleId || 'submit-button-' + Date.now() + '-' + Math.floor(Math.random() * 100000);

	return {
		type: 'subin-submit-button',
		name: 'Submit button',
		tagName: 'button',
		classes: [styleClassName(styleId)],
		attributes: {
			type: 'button',
			'data-subin-component': 'submit-button',
			'data-subin-style-id': styleId,
			'data-subin-button-text': buttonText || 'Submit',
		},
		components: escapeHtml(buttonText || 'Submit'),
		style: componentStyle || {},
		stylable: subinButtonStyleProps(),
		resizable: subinFlowResizableOptions(),
	};
}

function defaultFieldLabel(fieldType) {
	var labels = {
		text: 'Text field',
		email: 'Email address',
		phone: 'Phone number',
		date: 'Date',
		textarea: 'Message',
		select: 'Select field',
		radio: 'Radio field',
		checkbox: 'Checkbox field',
		consent: 'I agree to be contacted about this request.',
		hidden: 'Hidden field',
	};

	return labels[fieldType] || 'Field';
}

function fieldTypeLabel(fieldType) {
	var labels = {
		text: 'Text field',
		email: 'Email field',
		phone: 'Phone field',
		date: 'Date field',
		textarea: 'Textarea field',
		select: 'Select field',
		radio: 'Radio field',
		checkbox: 'Checkbox field',
		consent: 'Consent checkbox',
		hidden: 'Hidden field',
	};

	return labels[fieldType] || 'Field';
}

function humanReadableComponentLabel(component, subinType) {
	var attributes = component && component.getAttributes ? component.getAttributes() : {};
	var labels = {
		'form-wrapper': 'Form',
		row: 'Row',
		'submit-button': 'Submit button',
		'text-block': 'Text block',
		'html-block': 'HTML block',
		notice: 'Notice block',
		divider: 'Divider',
		privacy: 'Privacy note',
		section: 'Section',
		spacer: 'Spacer',
		'image-block': 'Image block',
	};

	if (subinType === 'field') {
		return fieldTypeLabel(attributes['data-subin-field-type'] || 'text');
	}

	return labels[subinType] || 'Element';
}

function updateFieldLayerName(component) {
	var attributes = component && component.getAttributes ? component.getAttributes() : {};

	if (component && component.set) {
		component.set('name', fieldTypeLabel(attributes['data-subin-field-type'] || 'text'));
	}
}

function fieldRootStyle(designStyles, fieldStyles) {
	var style = Object.assign({}, styleFromDesignSettings(designStyles.field_group, 'field_group'));
	var perFieldGroup = styleFromDesignSettings(fieldStyles.field_group, 'field_group');

	Object.keys(perFieldGroup).forEach(function (property) {
		style[property] = perFieldGroup[property];
	});

	return style;
}

function styleFromDesignSettings(settings, part) {
	var style = {};

	if (!settings || typeof settings !== 'object') {
		return style;
	}

	addColorStyle(style, settings, 'background_color', 'background-color');
	addColorStyle(style, settings, 'text_color', 'color');
	addColorStyle(style, settings, 'border_color', 'border-color');
	addPxStyle(style, settings, 'border_width', 'border-width');
	if (settings.border_width) {
		style['border-style'] = 'solid';
	}
	addPxStyle(style, settings, 'border_radius', 'border-radius');
	addPxStyle(style, settings, 'padding', 'padding');
	addPxStyle(style, settings, 'padding_x', 'padding-left');
	addPxStyle(style, settings, 'padding_x', 'padding-right');
	addPxStyle(style, settings, 'padding_y', 'padding-top');
	addPxStyle(style, settings, 'padding_y', 'padding-bottom');
	addPxStyle(style, settings, 'margin_bottom', 'margin-bottom');
	addPxStyle(style, settings, 'font_size', 'font-size');
	addPxStyle(style, settings, 'max_width', 'max-width');
	addChoiceStyle(style, settings, 'font_weight', 'font-weight', ['300', '400', '500', '600', '700', '800']);
	addChoiceStyle(style, settings, 'text_align', 'text-align', ['left', 'center', 'right']);

	if (typeof settings.line_height === 'number' || typeof settings.line_height === 'string') {
		var lineHeight = parseFloat(settings.line_height);
		if (lineHeight >= 1 && lineHeight <= 2.2) {
			style['line-height'] = String(lineHeight);
		}
	}

	if (settings.width === 'full') {
		style.display = 'block';
		style.width = '100%';
		style['max-width'] = '100%';
	} else if (settings.width === 'auto') {
		style.width = 'auto';
	} else if (['25', '33', '50', '66', '75', '100'].indexOf(String(settings.width)) !== -1) {
		style.display = 'block';
		style.width = String(settings.width) + '%';
		style['max-width'] = '100%';
	}

	if (part === 'submit_button' && settings.button_width === 'full') {
		style.display = 'block';
		style.width = '100%';
	}

	return style;
}

function addColorStyle(style, settings, key, property) {
	var value = settings[key];
	if (typeof value === 'string' && /^#(?:[a-f0-9]{3}|[a-f0-9]{6})$/i.test(value)) {
		style[property] = value;
	}
}

function addPxStyle(style, settings, key, property) {
	if (settings[key] !== undefined && settings[key] !== null && settings[key] !== '' && !isNaN(settings[key])) {
		style[property] = Math.max(0, parseInt(settings[key], 10)) + 'px';
	}
}

function addChoiceStyle(style, settings, key, property, allowed) {
	var value = String(settings[key] || '');
	if (allowed.indexOf(value) !== -1) {
		style[property] = value;
	}
}

function subinFieldPreview(fieldType, label, placeholder, required, fieldKey, options, helpText, consentText, defaultValue) {
	var requiredMarker = required ? ' <span class="subin-field-required" aria-hidden="true">*</span>' : '';
	var helpHtml = helpText ? '<p class="subin-field-help">' + escapeHtml(helpText) + '</p>' : '';
	var choiceOptions = isChoiceField(fieldType) ? parseChoiceOptions(options) : [];
	var choiceHtml = '';
	var inputType = fieldType === 'email' ? 'email' : (fieldType === 'phone' ? 'tel' : (fieldType === 'date' ? 'date' : 'text'));
	var placeholderAttribute = placeholder ? ' placeholder="' + escapeHtml(placeholder) + '"' : '';

	if (fieldType === 'consent') {
		return '<label class="subin-checkbox-field subin-field-checkbox"><input class="subin-field-checkbox-box" type="checkbox" disabled="disabled" data-subin-preview-control="field" /><span><span class="subin-field-label">' + escapeHtml(consentText || label) + requiredMarker + '</span></span></label>' + helpHtml;
	}

	if (fieldType === 'checkbox') {
		choiceOptions.forEach(function (option) {
			choiceHtml += '<label class="subin-choice-row"><input type="checkbox" disabled="disabled" data-subin-preview-control="choice" /><span>' + escapeHtml(option.label) + '</span></label>';
		});
		return '<label class="subin-field-label">' + escapeHtml(label) + requiredMarker + '</label><div class="subin-field-options">' + choiceHtml + '</div>' + helpHtml;
	}

	if (fieldType === 'radio') {
		choiceOptions.forEach(function (option) {
			choiceHtml += '<label class="subin-choice-row"><input type="radio" disabled="disabled" data-subin-preview-control="choice" /><span>' + escapeHtml(option.label) + '</span></label>';
		});
		return '<label class="subin-field-label">' + escapeHtml(label) + requiredMarker + '</label><div class="subin-field-options">' + choiceHtml + '</div>' + helpHtml;
	}

	if (fieldType === 'select') {
		choiceHtml += '<option value="">' + escapeHtml(placeholder || 'Choose an option') + '</option>';
		choiceOptions.forEach(function (option) {
			choiceHtml += '<option value="' + escapeHtml(option.value) + '">' + escapeHtml(option.label) + '</option>';
		});
		return '<label class="subin-field-label">' + escapeHtml(label) + requiredMarker + '</label><select class="subin-field-input" disabled="disabled" data-subin-preview-control="choice">' + choiceHtml + '</select>' + helpHtml;
	}

	if (fieldType === 'hidden') {
		return '<div class="subin-hidden-field-preview">Hidden field: ' + escapeHtml(fieldKey || label) + (defaultValue ? ' = ' + escapeHtml(defaultValue) : '') + '</div>';
	}

	var control = fieldType === 'textarea'
		? '<textarea class="subin-field-textarea" disabled="disabled" readonly="readonly" data-subin-preview-control="field"' + placeholderAttribute + '></textarea>'
		: '<input class="subin-field-input" type="' + inputType + '" disabled="disabled" readonly="readonly" data-subin-preview-control="field"' + placeholderAttribute + ' />';

	return '<label class="subin-field-label">' + escapeHtml(label) + requiredMarker + '</label>' + control + helpHtml;
}

function contentBlockClasses(type, componentName) {
	var classes = ['subin-content-block', 'subin-content-block--' + componentName];

	if (type) {
		classes.push(type);
	}

	return classes;
}

function escapeHtml(value) {
	return String(value)
		.replace(/&/g, '&amp;')
		.replace(/</g, '&lt;')
		.replace(/>/g, '&gt;')
		.replace(/"/g, '&quot;')
		.replace(/'/g, '&#039;');
}
