'use strict';

function registerSubmissionInboxComponents(editor) {
	var domComponents = editor.DomComponents;
	registerSubmissionInboxTraits(editor);

	domComponents.addType('subin-row', {
		isComponent: function (element) {
			return element.getAttribute && element.getAttribute('data-subin-component') === 'row';
		},
		model: {
			defaults: {
				tagName: 'div',
				attributes: {
					'data-subin-component': 'row',
				},
				components: [],
				draggable: true,
				droppable: true,
				selectable: true,
				stylable: subinRowStyleProps(),
				name: 'Row',
				traits: withBackgroundMediaTrait([
					{
						type: 'subin-row-actions',
						name: 'subin-row-actions',
						label: 'Row actions',
					},
				]),
			},
			init: function () {
				addClasses(this, ['subin-gjs-row']);
				ensureRowId(this);
				ensureUniqueStyleTarget(this, 'row');
				this.set('droppable', function (target) {
					return isFlowLayoutComponent(target);
				});
				this.set('stylable', subinRowStyleProps());
				this.set('traits', withBackgroundMediaTrait([
					{
						type: 'subin-row-actions',
						name: 'subin-row-actions',
						label: 'Row actions',
					},
				]));
				configureBackgroundMediaComponent(this);
				this.set('name', 'Row');
			},
		},
	});

	domComponents.addType('subin-field', {
		isComponent: function (element) {
			return element.getAttribute && element.getAttribute('data-subin-component') === 'field';
		},
		model: {
			defaults: {
				tagName: 'div',
				attributes: {
					'data-subin-component': 'field',
					'data-subin-field-type': 'text',
					'data-subin-field-key': 'text_field',
				},
				components: subinFieldPreview('text', 'Text field'),
				draggable: true,
				droppable: false,
				selectable: true,
				stylable: subinFieldStyleProps(),
				resizable: subinFlowResizableOptions(),
				name: 'Text field',
				traits: subinFieldTraits({
					'data-subin-field-type': 'text',
					'data-subin-field-key': 'text_field',
					'data-subin-label': 'Text field',
				}),
			},
			init: function () {
				var attributes = this.getAttributes();

				if (!attributes['data-subin-field-id'] && !attributes['data-subin-temp-id']) {
					this.addAttributes({
						'data-subin-temp-id': 'subin-field-' + Date.now() + '-' + Math.floor(Math.random() * 100000),
					});
				}

				addClasses(this, ['subin-form-field', 'subin-field-group']);
				ensureUniqueStyleTarget(this, 'field');
				configureFlowLayoutComponent(this);
				configureFieldComponentTraits(this);
				lockFieldPreviewChildren(this);
				updateFieldLayerName(this);
				this.on('change:attributes', function () {
					updateFieldComponentPreview(this);
				});
			},
		},
		view: {
			events: {
				click: 'selectFieldComponent',
			},
			selectFieldComponent: function (event) {
				if (event && event.target !== this.el && editor && editor.select) {
					editor.select(this.model);
				}
			},
		},
	});

	domComponents.addType('subin-submit-button', {
		isComponent: function (element) {
			return element.getAttribute && element.getAttribute('data-subin-component') === 'submit-button';
		},
		model: {
			defaults: {
				tagName: 'button',
				attributes: {
					type: 'button',
					'data-subin-component': 'submit-button',
				},
				components: 'Submit',
				draggable: true,
				droppable: false,
				selectable: true,
				stylable: subinButtonStyleProps(),
				resizable: subinFlowResizableOptions(),
				name: 'Submit button',
				traits: [
					{
						type: 'subin-text',
						name: 'data-subin-button-text',
						label: 'Button text',
					},
				],
			},
			init: function () {
				addClasses(this, ['subin-submit-button']);
				ensureUniqueStyleTarget(this, 'submit-button');
				configureFlowLayoutComponent(this);
				this.set('name', 'Submit button');
				if (!this.getAttributes()['data-subin-button-text']) {
					this.addAttributes({
						'data-subin-button-text': componentPlainText(this) || 'Submit',
					});
				}
				this.on('change:attributes:data-subin-button-text change:attributes', function () {
					updateSubmitButtonPreview(this);
				});
			},
		},
		view: {
			events: {
				click: 'selectSubmitComponent',
			},
			selectSubmitComponent: function (event) {
				if (event && event.target !== this.el && editor && editor.select) {
					editor.select(this.model);
				}
			},
		},
	});

	addSubmissionInboxContentType(domComponents, 'subin-text-block', 'text-block', '<p>Text block</p>');
	addSubmissionInboxContentType(domComponents, 'subin-html-block', 'html-block', '<p>HTML content</p>');
	addSubmissionInboxContentType(domComponents, 'subin-notice-block', 'notice', '<p>Notice message</p>');
	addSubmissionInboxContentType(domComponents, 'subin-divider-block', 'divider', '<hr />', false);
	addSubmissionInboxContentType(domComponents, 'subin-privacy-block', 'privacy', '<p>Privacy note</p>');
	addSubmissionInboxContentType(domComponents, 'subin-section-block', 'section', '<p>Section content</p>');
	addSubmissionInboxContentType(domComponents, 'subin-spacer-block', 'spacer', '<div class="subin-gjs-spacer-preview" aria-hidden="true"></div>', false);
	addSubmissionInboxContentType(domComponents, 'subin-image-block', 'image-block', '<div class="subin-gjs-image-placeholder" aria-hidden="true">Image</div>', false);
}

function addSubmissionInboxContentType(domComponents, type, componentName, previewContent, droppable) {
	domComponents.addType(type, {
		isComponent: function (element) {
			return element.getAttribute && element.getAttribute('data-subin-component') === componentName;
		},
		model: {
			defaults: {
				tagName: 'div',
				attributes: {
					'data-subin-component': componentName,
				},
				components: previewContent,
				draggable: true,
				droppable: false,
				selectable: true,
				stylable: subinContentStyleProps(componentName),
				resizable: subinFlowResizableOptions(),
				name: humanReadableComponentLabel(null, componentName),
				traits: subinContentTraits(componentName),
			},
			init: function () {
				addClasses(this, contentBlockClasses(type, componentName));
				ensureUniqueStyleTarget(this, componentName);
				configureFlowLayoutComponent(this);
				configureContentComponentTraits(this, componentName);
				configureBackgroundMediaComponent(this);
				lockContentPreviewChildren(this);
				this.set('name', humanReadableComponentLabel(this, componentName));
			},
		},
		view: {
			events: {
				click: 'selectContentComponent',
			},
			selectContentComponent: function (event) {
				if (event && event.target !== this.el && editor && editor.select) {
					editor.select(this.model);
				}
			},
		},
	});
}

var subinFlowSnapTimers = {};
var subinFlowSnapping = {};
var subinFlowWrapping = false;

function initSubmissionInboxFlowLayout(editor) {
	if (!editor || !editor.on) {
		return;
	}

	editor.on('component:add', function (component) {
		configureFlowLayoutComponent(component);
		ensureComponentLivesInExplicitRow(component);
	});

	editor.on('component:styleUpdate:width', function (component) {
		scheduleFlowWidthSnap(component);
	});
}

function configureFlowLayoutComponent(component) {
	if (!isFlowLayoutComponent(component)) {
		return;
	}

	component.set('draggable', true);
	component.set('droppable', false);
	component.set('resizable', subinFlowResizableOptions());

	var attributes = component.getAttributes ? component.getAttributes() : {};
	if (!attributes['data-subin-layout-span']) {
		var span = spanFromWidthValue(component.getStyle ? component.getStyle().width : '', component);
		component.addAttributes({
			'data-subin-layout-span': String(span || 12),
		});
	}
}

function isFlowLayoutComponent(component) {
	if (!component || !component.getAttributes) {
		return false;
	}

	var attributes = component.getAttributes();
	var type = component.get ? component.get('type') : '';
	var subinType = attributes['data-subin-component'] || subinComponentNameFromType(type);

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

function isRowComponent(component) {
	if (!component || !component.getAttributes) {
		return false;
	}

	var attributes = component.getAttributes();
	var type = component.get ? component.get('type') : '';
	return attributes['data-subin-component'] === 'row' || type === 'subin-row';
}

function isFormWrapperComponent(component) {
	if (!component || !component.getAttributes) {
		return false;
	}

	var attributes = component.getAttributes();
	var type = component.get ? component.get('type') : '';
	return attributes['data-subin-component'] === 'form-wrapper' || type === 'wrapper';
}

function ensureComponentLivesInExplicitRow(component) {
	var parent;
	var collection;
	var index;
	var rowData;

	if (subinFlowWrapping || !isFlowLayoutComponent(component) || !component.parent) {
		return;
	}

	parent = component.parent();
	if (!isFormWrapperComponent(parent)) {
		return;
	}

	collection = parent.components && parent.components();
	if (!collection) {
		return;
	}

	index = collection.indexOf ? collection.indexOf(component) : undefined;
	rowData = subinRowComponent([component.toJSON ? component.toJSON() : {}]);

	subinFlowWrapping = true;
	component.remove();
	collection.add(rowData, typeof index === 'number' && index >= 0 ? { at: index } : {});
	subinFlowWrapping = false;
}

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

	if (attributes['data-subin-row-id']) {
		return;
	}

	component.addAttributes({
		'data-subin-row-id': 'row-' + Date.now() + '-' + Math.floor(Math.random() * 100000),
	});
}

function selectedRow(component) {
	if (isRowComponent(component)) {
		return component;
	}

	var parent = component && component.parent ? component.parent() : null;
	return isRowComponent(parent) ? parent : null;
}

function addRowNear(component, position) {
	var row = selectedRow(component);
	var parent = row && row.parent ? row.parent() : null;
	var collection = parent && parent.components ? parent.components() : null;
	var index = collection && collection.indexOf ? collection.indexOf(row) : -1;

	if (!collection || index < 0) {
		return;
	}

	collection.add(subinRowComponent([]), {
		at: position === 'above' ? index : index + 1,
	});
}

function deleteRowIfEmpty(component) {
	var row = selectedRow(component);
	var children = row && row.components ? row.components() : null;

	if (!row || !children || children.length) {
		return;
	}

	row.remove();
}

function scheduleFlowWidthSnap(component) {
	if (!isFlowLayoutComponent(component) || !component.cid || subinFlowSnapping[component.cid]) {
		return;
	}

	window.clearTimeout(subinFlowSnapTimers[component.cid]);
	subinFlowSnapTimers[component.cid] = window.setTimeout(function () {
		snapFlowComponentWidth(component);
	}, 180);
}

function snapFlowComponentWidth(component) {
	if (!isFlowLayoutComponent(component) || !component.cid || subinFlowSnapping[component.cid]) {
		return;
	}

	var style = component.getStyle ? component.getStyle() : {};
	var span = spanFromWidthValue(style.width, component);

	if (!span) {
		return;
	}

	var snappedWidth = flowWidthFromSpan(span);
	var attributes = component.getAttributes ? component.getAttributes() : {};

	if (style.width === snappedWidth && attributes['data-subin-layout-span'] === String(span)) {
		return;
	}

	subinFlowSnapping[component.cid] = true;
	component.addAttributes({
		'data-subin-layout-span': String(span),
		'data-subin-layout-width': snappedWidth,
	});
	component.setStyle(Object.assign({}, style, {
		width: snappedWidth,
	}));
	window.setTimeout(function () {
		delete subinFlowSnapping[component.cid];
	}, 0);
}

function spanFromWidthValue(width, component) {
	var value = String(width || '').trim();
	var percent = 0;

	if (!value || value === 'auto') {
		return 0;
	}

	if (/^calc\(/i.test(value)) {
		var calcMatch = value.match(/calc\(\s*([0-9.]+)%/i);
		percent = calcMatch ? parseFloat(calcMatch[1]) : 0;
	} else if (/%$/.test(value)) {
		percent = parseFloat(value);
	} else if (/px$/i.test(value)) {
		var parentWidth = flowParentWidth(component);
		if (!parentWidth) {
			return 0;
		}
		percent = parseFloat(value) / parentWidth * 100;
	} else {
		return 0;
	}

	if (!isFinite(percent) || percent <= 0) {
		return 0;
	}

	return Math.max(1, Math.min(12, Math.round(percent / 100 * 12)));
}

function flowWidthFromSpan(span) {
	var width = span / 12 * 100;
	var widthValue = String(Math.round(width * 10000) / 10000).replace(/\.0+$/, '');
	var gapShare = Math.round((16 * (12 - span) / 12) * 10000) / 10000;

	if (span >= 12 || gapShare <= 0) {
		return widthValue + '%';
	}

	return 'calc(' + widthValue + '% - ' + gapShare + 'px)';
}

function flowParentWidth(component) {
	var parent = component && component.parent ? component.parent() : null;
	var parentEl = parent && parent.view && parent.view.el ? parent.view.el : null;

	return parentEl && parentEl.clientWidth ? parentEl.clientWidth : 0;
}

function addClasses(component, classes) {
	classes.forEach(function (className) {
		if ((component.getClasses ? component.getClasses() : []).indexOf(className) === -1) {
			component.addClass(className);
		}
	});
}

function configureBackgroundMediaComponent(component) {
	if (!isBackgroundMediaComponent(component)) {
		return;
	}

	component.set('traits', withBackgroundMediaTrait(component.get('traits') || []));
	applyBackgroundMediaStyle(component);
	if (component.__subinBackgroundMediaHandler && component.off) {
		component.off('change:attributes', component.__subinBackgroundMediaHandler);
	}
	component.__subinBackgroundMediaHandler = function () {
		applyBackgroundMediaStyle(component);
	};
	component.on('change:attributes', component.__subinBackgroundMediaHandler);
}

function isBackgroundMediaComponent(component) {
	if (!component || !component.getAttributes) {
		return false;
	}

	var attributes = component.getAttributes();
	var type = component.get ? component.get('type') : '';
	var subinType = attributes['data-subin-component'] || subinComponentNameFromType(type);

	return [
		'form-wrapper',
		'row',
		'text-block',
		'html-block',
		'notice',
		'privacy',
	].indexOf(subinType) !== -1;
}

function isBackgroundMediaComponentData(subinType) {
	return [
		'text-block',
		'html-block',
		'notice',
		'privacy',
	].indexOf(String(subinType || '')) !== -1;
}

function applyBackgroundMediaStyle(component) {
	var attributes = component && component.getAttributes ? component.getAttributes() : {};
	var style = component && component.getStyle ? Object.assign({}, component.getStyle()) : {};
	var url = sanitizeMediaUrl(attributes['data-subin-bg-image-url'] || '');

	delete style['background-image'];
	delete style['background-size'];
	delete style['background-position'];
	delete style['background-repeat'];

	if (url) {
		style['background-image'] = 'url("' + url.replace(/"/g, '%22') + '")';
		style['background-size'] = sanitizeBackgroundSize(attributes['data-subin-bg-size'] || 'cover');
		style['background-position'] = sanitizeBackgroundPosition(attributes['data-subin-bg-position'] || 'center');
		style['background-repeat'] = sanitizeBackgroundRepeat(attributes['data-subin-bg-repeat'] || 'no-repeat');
	}

	if (component && component.setStyle) {
		component.setStyle(style);
	}
}

function renderBackgroundMediaTrait(el, attributes) {
	var url = sanitizeMediaUrl(attributes['data-subin-bg-image-url'] || '');
	var size = sanitizeBackgroundSize(attributes['data-subin-bg-size'] || 'cover');
	var position = sanitizeBackgroundPosition(attributes['data-subin-bg-position'] || 'center');
	var repeat = sanitizeBackgroundRepeat(attributes['data-subin-bg-repeat'] || 'no-repeat');

	el.innerHTML = [
		'<div class="subin-gjs-media-trait__preview' + (url ? '' : ' subin-gjs-media-trait__preview--empty') + '"' + (url ? ' style="background-image:url(&quot;' + escapeHtml(url) + '&quot;)"' : '') + '>',
		url ? '' : '<span>No image selected</span>',
		'</div>',
		'<div class="subin-gjs-media-trait__actions">',
		'<button type="button" class="button button-small" data-subin-bg-select>' + (url ? 'Replace image' : 'Select image') + '</button>',
		'<button type="button" class="button button-small" data-subin-bg-remove' + (url ? '' : ' disabled') + '>Remove</button>',
		'</div>',
		'<label>Background size<select data-subin-bg-setting="data-subin-bg-size">' + selectOptions(['cover', 'contain', 'auto'], size) + '</select></label>',
		'<label>Background position<select data-subin-bg-setting="data-subin-bg-position">' + selectOptions(['center', 'top', 'bottom', 'left', 'right'], position) + '</select></label>',
		'<label>Background repeat<select data-subin-bg-setting="data-subin-bg-repeat">' + selectOptions(['no-repeat', 'repeat', 'repeat-x', 'repeat-y'], repeat) + '</select></label>',
	].join('');
}

function renderImageMediaTrait(el, attributes) {
	var url = sanitizeMediaUrl(attributes['data-subin-media-url'] || '');

	el.innerHTML = [
		'<div class="subin-gjs-media-trait__preview' + (url ? '' : ' subin-gjs-media-trait__preview--empty') + '"' + (url ? ' style="background-image:url(&quot;' + escapeHtml(url) + '&quot;)"' : '') + '>',
		url ? '' : '<span>No image selected</span>',
		'</div>',
		'<div class="subin-gjs-media-trait__actions">',
		'<button type="button" class="button button-small" data-subin-image-select>' + (url ? 'Replace image' : 'Select image') + '</button>',
		'<button type="button" class="button button-small" data-subin-image-remove' + (url ? '' : ' disabled') + '>Remove</button>',
		'</div>',
	].join('');
}

function selectOptions(options, current) {
	return options.map(function (option) {
		return '<option value="' + escapeHtml(option) + '"' + (option === current ? ' selected' : '') + '>' + escapeHtml(option) + '</option>';
	}).join('');
}

function openBackgroundMediaFrame(component, elInput) {
	var frame;

	openBackgroundMediaUploader(component, elInput);
	return;

	if (!window.wp || !window.wp.media) {
		return;
	}

	if (window.wp.media.editor && window.wp.media.editor.open) {
		openBackgroundMediaEditor(component, elInput);
		return;
	}

	frame = window.SUBINBackgroundMediaFrame;

	if (!frame) {
		frame = window.wp.media({
			frame: 'select',
			title: 'Select background image',
			button: {
				text: 'Use image',
			},
			multiple: false,
			library: {
				type: 'image',
			},
		});

		frame.on('open ready', function () {
			prepareBackgroundMediaFrame(frame);
		});

		frame.on('select', function () {
			var target = frame.__subinBackgroundTarget || {};
			var attachment = frame.state().get('selection').first();
			var data = attachment ? attachment.toJSON() : {};
			var url = sanitizeMediaUrl(data.url || '');

			if (!url || !target.component) {
				return;
			}

			target.component.addAttributes({
				'data-subin-bg-image-id': data.id ? String(parseInt(data.id, 10) || 0) : '',
				'data-subin-bg-image-url': url,
				'data-subin-bg-size': sanitizeBackgroundSize((target.component.getAttributes && target.component.getAttributes()['data-subin-bg-size']) || 'cover'),
				'data-subin-bg-position': sanitizeBackgroundPosition((target.component.getAttributes && target.component.getAttributes()['data-subin-bg-position']) || 'center'),
				'data-subin-bg-repeat': sanitizeBackgroundRepeat((target.component.getAttributes && target.component.getAttributes()['data-subin-bg-repeat']) || 'no-repeat'),
			});
			renderBackgroundMediaTrait(target.elInput, target.component.getAttributes ? target.component.getAttributes() : {});
		});

		window.SUBINBackgroundMediaFrame = frame;
	}

	frame.__subinBackgroundTarget = {
		component: component,
		elInput: elInput,
	};
	frame.open();
	window.setTimeout(function () {
		prepareBackgroundMediaFrame(frame);
	}, 50);
}

function prepareBackgroundMediaFrame(frame) {
	var state = frame && frame.state ? frame.state() : null;
	var library = state && state.get ? state.get('library') : null;
	var selection = state && state.get ? state.get('selection') : null;

	if (!frame) {
		return;
	}

	if (frame.content && frame.content.mode) {
		frame.content.mode('browse');
	}

	if (selection && selection.reset) {
		selection.reset();
	}

	if (library && library.props && library.props.set) {
		library.props.set({
			type: 'image',
			uploadedTo: null,
		});
	}

	if (library && library.more) {
		library.more();
	}
}

function openBackgroundMediaUploader(component, elInput) {
	var input = document.createElement('input');

	input.type = 'file';
	input.accept = 'image/*';
	input.style.position = 'fixed';
	input.style.left = '-9999px';
	input.style.top = '0';
	input.addEventListener('change', function () {
		var file = input.files && input.files[0] ? input.files[0] : null;

		if (input.parentNode) {
			input.parentNode.removeChild(input);
		}

		if (!file) {
			return;
		}

		uploadBackgroundMediaFile(component, elInput, file);
	});

	document.body.appendChild(input);
	input.click();
}

function uploadBackgroundMediaFile(component, elInput, file) {
	var formData = readSubmissionInboxFormData();
	var ajax = formData && formData.ajax ? formData.ajax : {};
	var body = new window.FormData();
	var button = elInput && elInput.querySelector ? elInput.querySelector('[data-subin-bg-select]') : null;
	var originalText = button ? button.textContent : '';

	if (!file || !component || !ajax.media_nonce) {
		return;
	}

	body.append('action', ajax.media_action || 'subin_upload_design_studio_media');
	body.append('form_id', String(formData.id || ''));
	body.append('nonce', ajax.media_nonce || '');
	body.append('file', file);

	if (button) {
		button.disabled = true;
		button.textContent = 'Uploading...';
	}

	window.fetch(ajax.url || window.ajaxurl, {
		method: 'POST',
		credentials: 'same-origin',
		body: body,
	})
		.then(function (response) {
			return response.json().then(function (payload) {
				if (!response.ok || !payload || payload.success !== true) {
					throw new Error(grapesJSAjaxMessage(payload) || 'Image could not be uploaded.');
				}

				return payload;
			});
		})
		.then(function (payload) {
			var data = payload && payload.data ? payload.data : {};
			applyBackgroundMediaSelection(component, elInput, data);
		})
		.catch(function (error) {
			window.alert(error && error.message ? error.message : 'Image could not be uploaded.');
		})
		.finally(function () {
			if (button) {
				button.disabled = false;
				button.textContent = originalText || 'Select image';
			}
		});
}

function openImageMediaFrame(component, elInput) {
	var input = document.createElement('input');

	input.type = 'file';
	input.accept = 'image/*';
	input.style.position = 'fixed';
	input.style.left = '-9999px';
	input.style.top = '0';
	input.addEventListener('change', function () {
		var file = input.files && input.files[0] ? input.files[0] : null;

		if (input.parentNode) {
			input.parentNode.removeChild(input);
		}

		if (!file) {
			return;
		}

		uploadImageMediaFile(component, elInput, file);
	});

	document.body.appendChild(input);
	input.click();
}

function uploadImageMediaFile(component, elInput, file) {
	var formData = readSubmissionInboxFormData();
	var ajax = formData && formData.ajax ? formData.ajax : {};
	var body = new window.FormData();
	var button = elInput && elInput.querySelector ? elInput.querySelector('[data-subin-image-select]') : null;
	var originalText = button ? button.textContent : '';

	if (!file || !component || !ajax.media_nonce) {
		return;
	}

	body.append('action', ajax.media_action || 'subin_upload_design_studio_media');
	body.append('form_id', String(formData.id || ''));
	body.append('nonce', ajax.media_nonce || '');
	body.append('file', file);

	if (button) {
		button.disabled = true;
		button.textContent = 'Uploading...';
	}

	window.fetch(ajax.url || window.ajaxurl, {
		method: 'POST',
		credentials: 'same-origin',
		body: body,
	})
		.then(function (response) {
			return response.json().then(function (payload) {
				if (!response.ok || !payload || payload.success !== true) {
					throw new Error(grapesJSAjaxMessage(payload) || 'Image could not be uploaded.');
				}

				return payload;
			});
		})
		.then(function (payload) {
			var data = payload && payload.data ? payload.data : {};
			applyImageMediaSelection(component, elInput, data);
		})
		.catch(function (error) {
			window.alert(error && error.message ? error.message : 'Image could not be uploaded.');
		})
		.finally(function () {
			if (button) {
				button.disabled = false;
				button.textContent = originalText || 'Select image';
			}
		});
}

function applyBackgroundMediaSelection(component, elInput, data) {
	var url = sanitizeMediaUrl(data && data.url ? data.url : '');

	if (!url || !component) {
		return;
	}

	component.addAttributes({
		'data-subin-bg-image-id': data.id ? String(parseInt(data.id, 10) || 0) : '',
		'data-subin-bg-image-url': url,
		'data-subin-bg-size': sanitizeBackgroundSize((component.getAttributes && component.getAttributes()['data-subin-bg-size']) || 'cover'),
		'data-subin-bg-position': sanitizeBackgroundPosition((component.getAttributes && component.getAttributes()['data-subin-bg-position']) || 'center'),
		'data-subin-bg-repeat': sanitizeBackgroundRepeat((component.getAttributes && component.getAttributes()['data-subin-bg-repeat']) || 'no-repeat'),
	});
	renderBackgroundMediaTrait(elInput, component.getAttributes ? component.getAttributes() : {});
}

function applyImageMediaSelection(component, elInput, data) {
	var url = sanitizeMediaUrl(data && data.url ? data.url : '');

	if (!url || !component) {
		return;
	}

	component.addAttributes({
		'data-subin-media-id': data.id ? String(parseInt(data.id, 10) || 0) : '',
		'data-subin-media-url': url,
		'data-subin-media-alt': String(data.alt || data.title || '').replace(/<[^>]*>/g, '').trim(),
	});
	updateImageBlockPreview(component);
	renderImageMediaTrait(elInput, component.getAttributes ? component.getAttributes() : {});
}

function openBackgroundMediaEditor(component, elInput) {
	var editor = window.wp && window.wp.media ? window.wp.media.editor : null;
	var button = elInput && elInput.querySelector ? elInput.querySelector('[data-subin-bg-select]') : null;
	var previousHandler = editor ? editor.send.attachment : null;

	if (!editor || !editor.open) {
		return;
	}

	editor.send.attachment = function (props, attachment) {
		var data = attachment || {};
		var url = sanitizeMediaUrl(data.url || '');

		if (previousHandler) {
			editor.send.attachment = previousHandler;
		}

		if (!url || !component) {
			return;
		}

		applyBackgroundMediaSelection(component, elInput, data);
	};

	editor.open(button || document.body);
}

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

	var attributes = Object.assign({}, component.getAttributes());

	delete attributes['data-subin-bg-image-id'];
	delete attributes['data-subin-bg-image-url'];
	delete attributes['data-subin-bg-size'];
	delete attributes['data-subin-bg-position'];
	delete attributes['data-subin-bg-repeat'];

	component.set('attributes', attributes);
	applyBackgroundMediaStyle(component);
}

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

	var attributes = Object.assign({}, component.getAttributes());

	delete attributes['data-subin-media-id'];
	delete attributes['data-subin-media-url'];
	delete attributes['data-subin-media-alt'];

	component.set('attributes', attributes);
	updateImageBlockPreview(component);
}

function setBackgroundMediaSetting(component, name, value) {
	var next = {};

	if (!component || !component.addAttributes) {
		return;
	}

	if (name === 'data-subin-bg-size') {
		next[name] = sanitizeBackgroundSize(value);
	} else if (name === 'data-subin-bg-position') {
		next[name] = sanitizeBackgroundPosition(value);
	} else if (name === 'data-subin-bg-repeat') {
		next[name] = sanitizeBackgroundRepeat(value);
	} else {
		return;
	}

	component.addAttributes(next);
}

function sanitizeMediaUrl(value) {
	var url = String(value || '').trim();

	if (!/^https?:\/\//i.test(url) || /^(javascript|data):/i.test(url) || /\.(svg|svgz)(?:[?#].*)?$/i.test(url)) {
		return '';
	}

	if (!/\.(jpe?g|png|gif|webp|avif)(?:[?#].*)?$/i.test(url)) {
		return '';
	}

	return url.replace(/[\u0000-\u001f"'<>\\]/g, '');
}

function sanitizeBackgroundSize(value) {
	return ['cover', 'contain', 'auto'].indexOf(String(value || '')) !== -1 ? String(value) : 'cover';
}

function sanitizeBackgroundPosition(value) {
	return ['center', 'top', 'bottom', 'left', 'right'].indexOf(String(value || '')) !== -1 ? String(value) : 'center';
}

function sanitizeBackgroundRepeat(value) {
	return ['no-repeat', 'repeat', 'repeat-x', 'repeat-y'].indexOf(String(value || '')) !== -1 ? String(value) : 'no-repeat';
}

function ensureUniqueStyleTarget(component, prefix) {
	var attributes = component.getAttributes();
	var styleId = attributes['data-subin-style-id'] || buildStyleId(attributes, prefix);
	var className = styleClassName(styleId);

	component.addAttributes({
		'data-subin-style-id': styleId,
	});

	if ((component.getClasses ? component.getClasses() : []).indexOf(className) === -1) {
		component.addClass(className);
	}
}

function buildStyleId(attributes, prefix) {
	if (attributes['data-subin-field-id']) {
		return 'field-' + attributes['data-subin-field-id'];
	}

	if (attributes['data-subin-temp-id']) {
		return attributes['data-subin-temp-id'];
	}

	if (attributes['data-subin-field-key']) {
		return 'field-' + attributes['data-subin-field-key'];
	}

	return prefix + '-' + Date.now() + '-' + Math.floor(Math.random() * 100000);
}

function styleClassName(styleId) {
	return 'subin-gjs-component-' + String(styleId).toLowerCase().replace(/[^a-z0-9_-]+/g, '-').replace(/^-+|-+$/g, '');
}
