/**
 * Custom Fields - field control behavior (media picker + color picker).
 * Loaded on the builder screen and on post/term edit screens.
 */
(function ($) {
	'use strict';

	var cfg = window.wppsCfFields || {};

	function revealBlockEditorMetaBoxes() {
		var hasCustomFieldsBox = document.querySelector('.edit-post-layout__metaboxes .cf-postbox');
		if (!hasCustomFieldsBox) {
			return;
		}

		var liner = document.querySelector('.edit-post-layout__metaboxes');
		if (liner && liner.hasAttribute('hidden')) {
			liner.removeAttribute('hidden');
		}
	}

	function initColorPickers(context) {
		if (typeof $.fn.wpColorPicker !== 'function') {
			return;
		}
		$(context)
			.find('.cf-color-field')
			.not('.wp-color-picker')
			.each(function () {
				var $input = $(this);
				var palettes = String($input.data('palettes') || '')
					.split(',')
					.map(function (color) {
						return $.trim(color);
					})
					.filter(Boolean);
				var options = {};
				if (palettes.length) {
					options.palettes = palettes;
				}
				if ($input.data('alphaEnabled')) {
					options.alpha = true;
				}
				$input.wpColorPicker(options);
				if (String($input.data('showColorWheel')) === '0') {
					$input.closest('.wp-picker-container').addClass('cf-color-picker--no-wheel');
				}
			});
	}

	function openMediaFrame($wrap) {
		var type = $wrap.data('type') || 'file';
		var $input = $wrap.find('.cf-media__input');
		var $preview = $wrap.find('.cf-media__preview');
		var $clear = $wrap.find('.cf-media__clear');
		var previewSize = String($wrap.data('cfPreviewSize') || 'thumbnail');

		var libraryType = type === 'image' ? 'image' : '';
		var library = libraryType ? { type: libraryType } : {};
		var mimeTypes = normalizeMediaMimeTypes($wrap.data('cfMimeTypes'));
		if (type === 'image') {
			mimeTypes = imageMimeTypesOnly(mimeTypes);
		}
		if (mimeTypes.length) {
			library.type = mimeTypes;
		}
		if (
			$wrap.data('cfLibrary') === 'uploaded_to' &&
			wp.media.view &&
			wp.media.view.settings &&
			wp.media.view.settings.post &&
			wp.media.view.settings.post.id
		) {
			library.uploadedTo = wp.media.view.settings.post.id;
		}
		var frame = wp.media({
			title: type === 'image' ? 'Select Image' : 'Select File',
			library: library,
			multiple: false,
		});

		frame.on('select', function () {
			var attachment = frame.state().get('selection').first().toJSON();
			$input.val(attachment.id);
			if (type === 'image') {
				var url =
					attachment.sizes && attachment.sizes[previewSize]
						? attachment.sizes[previewSize].url
						: attachment.sizes && attachment.sizes.thumbnail
							? attachment.sizes.thumbnail.url
						: attachment.url;
				$preview.empty().append(
					$('<img>').addClass('cf-media-preview__img').attr({ src: url || '', alt: '' })
				);
			} else {
				$preview.empty().append(
					$('<span>').addClass('cf-media-preview__file').text(attachment.filename || '')
				);
			}
			$clear.prop('hidden', false);
		});

		frame.open();
	}

	function normalizeMediaMimeTypes(raw) {
		var extensionMap = {
			jpg: 'image/jpeg',
			jpeg: 'image/jpeg',
			png: 'image/png',
			gif: 'image/gif',
			webp: 'image/webp',
			svg: 'image/svg+xml',
			pdf: 'application/pdf',
			txt: 'text/plain',
			csv: 'text/csv',
			doc: 'application/msword',
			docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
			xls: 'application/vnd.ms-excel',
			xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
			zip: 'application/zip',
			mp3: 'audio/mpeg',
			wav: 'audio/wav',
			mp4: 'video/mp4',
			mov: 'video/quicktime',
		};

		return String(raw || '')
			.split(/[\s,]+/)
			.map(function (item) {
				item = $.trim(item).toLowerCase().replace(/^\./, '');
				return extensionMap[item] || item;
			})
			.filter(function (item) {
				return item && (item.indexOf('/') !== -1 || item === 'image' || item === 'audio' || item === 'video');
			});
	}

	function imageMimeTypesOnly(types) {
		var filtered = types.filter(function (item) {
			return item === 'image' || item.indexOf('image/') === 0;
		});
		return filtered.length ? filtered : ['image'];
	}

	function setIconPickerValue($picker, value) {
		var normalized = String(value || '');
		var $preview = $picker.find('.cf-icon-picker__preview');

		$picker.find('.cf-icon-picker__value').val(normalized).attr('value', normalized);
		if (/^https?:\/\//i.test(normalized)) {
			$preview
				.attr('class', 'cf-icon-picker__preview cf-icon-picker__preview--image')
				.empty()
				.append($('<img>').attr({ src: normalized, alt: '' }));
			return;
		}

		$preview
			.attr('class', 'cf-icon-picker__preview dashicons ' + (normalized || 'dashicons-marker'))
			.empty();
	}

	function activateIconPickerTab($picker, tab) {
		var target = String(tab || '');
		if (!target) {
			return;
		}

		$picker.find('.cf-icon-picker__tab').each(function () {
			var active = String($(this).data('iconTab')) === target;
			$(this).toggleClass('is-active', active).attr('aria-selected', active ? 'true' : 'false');
		});
		$picker.find('.cf-icon-picker__panel').each(function () {
			$(this).prop('hidden', String($(this).data('iconPanel')) !== target);
		});
	}

	function initIconPickers(context) {
		$(context)
			.find('.cf-icon-picker')
			.addBack('.cf-icon-picker')
			.each(function () {
				var $picker = $(this);
				if ($picker.data('cfIconPickerInit')) {
					return;
				}
				$picker.data('cfIconPickerInit', true);

				var current = String($picker.find('.cf-icon-picker__value').val() || '');
				var initialTab = /^https?:\/\//i.test(current)
					? ($picker.find('[data-icon-panel="url"]').length ? 'url' : 'dashicons')
					: 'dashicons';
				if (!$picker.find('[data-icon-panel="' + initialTab + '"]').length) {
					initialTab = String($picker.find('.cf-icon-picker__panel').first().data('iconPanel') || '');
				}
				activateIconPickerTab($picker, initialTab);
				setIconPickerValue($picker, current);
			});
	}

	function syncGalleryInput($gallery) {
		var ids = [];
		$gallery.find('.cf-gallery-preview__item').each(function () {
			var id = $(this).data('id');
			if (id) {
				ids.push(String(id));
			}
		});
		$gallery.find('.cf-gallery__input').val(ids.join(','));
		$gallery.find('.cf-gallery__clear').prop('hidden', ids.length === 0);
	}

	function openGalleryFrame($gallery) {
		var $preview = $gallery.find('.cf-gallery__preview');
		var previewSize = String($gallery.data('cfPreviewSize') || 'thumbnail');
		var insert = String($gallery.data('cfInsert') || 'append');
		var mimeTypes = imageMimeTypesOnly(normalizeMediaMimeTypes($gallery.data('cfMimeTypes')));
		var library = { type: mimeTypes };
		if (
			$gallery.data('cfLibrary') === 'uploaded_to' &&
			wp.media.view &&
			wp.media.view.settings &&
			wp.media.view.settings.post &&
			wp.media.view.settings.post.id
		) {
			library.uploadedTo = wp.media.view.settings.post.id;
		}
		var frame = wp.media({
			title: 'Select Images',
			library: library,
			multiple: true,
		});

		frame.on('select', function () {
			var items = [];
			frame.state().get('selection').each(function (attachment) {
				var data = attachment.toJSON();
				var id = data.id;
				if ($preview.find('[data-id="' + id + '"]').length) {
					return;
				}
				var url =
					data.sizes && data.sizes[previewSize]
						? data.sizes[previewSize].url
						: data.sizes && data.sizes.thumbnail
							? data.sizes.thumbnail.url
							: data.url;
				items.push(
					$('<span>')
						.addClass('cf-gallery-preview__item')
						.attr('data-id', id)
						.append($('<img>').addClass('cf-gallery-preview__img').attr({ src: url, alt: '' }))
						.append(
							$('<button>')
								.attr({ type: 'button', 'aria-label': 'Remove' })
								.addClass('cf-gallery-preview__remove')
								.html('&times;')
						)
				);
			});
			if (items.length) {
				if (insert === 'prepend') {
					$preview.prepend(items);
				} else {
					$preview.append(items);
				}
			}
			syncGalleryInput($gallery);
		});

		frame.open();
	}

	function fetchOembed($input) {
		var url = $.trim($input.val());
		var $preview = $input.closest('.cf-field').find('.cf-oembed-preview');
		var request = { url: url };
		var maxwidth = parseInt($input.data('maxwidth'), 10);
		var maxheight = parseInt($input.data('maxheight'), 10);
		if (!url || !cfg.oembedUrl) {
			$preview.empty();
			return;
		}
		if (!isNaN(maxwidth) && maxwidth > 0) {
			request.maxwidth = maxwidth;
		} else {
			request.maxwidth = 640;
		}
		if (!isNaN(maxheight) && maxheight > 0) {
			request.maxheight = maxheight;
		}

		$preview.addClass('is-loading').empty().append(
			$('<span class="cf-oembed-preview__loading"></span>').text('Loading preview…')
		);

		$.get(cfg.oembedUrl, request)
			.done(function (response) {
				if (response && response.html) {
					$preview.removeClass('is-loading').html(response.html);
				} else {
					$preview.removeClass('is-loading').empty();
				}
			})
			.fail(function () {
				$preview.removeClass('is-loading').empty();
			});
	}

	function rebuildSelectOptions($select, results, multiple, placeholder) {
		var selected = multiple
			? $select.val() || []
			: [String($select.val() || '')];
		var selectedLabels = {};
		if (!Array.isArray(selected)) {
			selected = [String(selected)];
		}
		$select.find('option:selected').each(function () {
			selectedLabels[String($(this).val())] = $(this).text();
		});

		$select.empty();
		if (!multiple) {
			$select.append($('<option>').val('').text(placeholder || '—'));
		}

		(results || []).forEach(function (item) {
			if (selected.indexOf(String(item.value)) !== -1) {
				delete selectedLabels[String(item.value)];
			}
		});
		Object.keys(selectedLabels).forEach(function (value) {
			if (!value) {
				return;
			}
			$select.append(
				$('<option>')
					.val(value)
					.text(selectedLabels[value])
					.prop('selected', true)
			);
		});

		(results || []).forEach(function (item) {
			var $option = $('<option>').val(item.value).text(item.label);
			if (item.image) {
				$option.attr('data-image', item.image);
			}
			if (selected.indexOf(String(item.value)) !== -1) {
				$option.prop('selected', true);
			}
			$select.append($option);
		});
		$select.trigger('change');
	}

	function refreshRelationshipPane($wrap) {
		var $select = $wrap.find('.cf-relational__select');
		var $list = $wrap.find('.cf-relationship-selected__list');
		var $empty = $wrap.find('.cf-relationship-selected__empty');
		var selected = [];

		if (!$list.length || !$select.length) {
			return;
		}

		$select.find('option:selected').each(function () {
			var value = String($(this).val());
			if (!value) {
				return;
			}
			selected.push({
				value: value,
				label: $.trim($(this).text()) || value,
				image: String($(this).attr('data-image') || ''),
			});
		});

		$list.empty();
		selected.forEach(function (item) {
			var $item = $('<li class="cf-relationship-selected__item">');
			if ($wrap.data('cfShowFeaturedImage') && item.image) {
				$item
					.addClass('has-image')
					.append($('<img class="cf-relationship-selected__image" alt="">').attr('src', item.image));
			}
			$item
				.append($('<span class="cf-relationship-selected__label">').text(item.label))
				.append(
					$('<button type="button" class="button-link cf-relationship-selected__remove">')
						.attr('aria-label', 'Remove ' + item.label)
						.attr('data-value', item.value)
						.text('×')
				)
				.appendTo($list);
		});

		$empty.prop('hidden', selected.length > 0);
		$wrap.find('.cf-relationship-selected__count').text(String(selected.length));
	}

	function initRelationshipPane($wrap) {
		var $select = $wrap.find('.cf-relational__select');
		if (String($wrap.data('cf-type')) !== 'relationship' || !$select.prop('multiple')) {
			return;
		}
		if ($wrap.data('cfRelationshipPaneInit')) {
			refreshRelationshipPane($wrap);
			return;
		}

		$wrap.data('cfRelationshipPaneInit', true);
		$wrap.addClass('cf-relational-search--relationship');
		var elements = String($wrap.data('cfRelationshipElements') || '').split(',');
		var filters = String($wrap.data('cfRelationshipFilters') || 'search,post_type,taxonomy').split(',');
		$wrap.data('cfShowFeaturedImage', elements.indexOf('featured_image') !== -1);
		if (filters.indexOf('search') === -1) {
			$wrap.addClass('cf-relational-search--no-search');
			$wrap.find('.cf-relational-search__input, .cf-relational-search__status').prop('hidden', true);
		}
		$select.wrap('<div class="cf-relationship-picker__available"></div>');
		$select
			.closest('.cf-relationship-picker__available')
			.before(
				'<div class="cf-relationship-picker__head">' +
					'<strong>Available</strong>' +
					'<span>Use search to narrow results.</span>' +
				'</div>'
			)
			.after(
				'<div class="cf-relationship-picker__selected">' +
					'<div class="cf-relationship-picker__head">' +
						'<strong>Selected <span class="cf-relationship-selected__count">0</span></strong>' +
						'<span>Remove items here without losing your search.</span>' +
					'</div>' +
					'<p class="cf-relationship-selected__empty">No posts selected.</p>' +
					'<ul class="cf-relationship-selected__list"></ul>' +
				'</div>'
			);

		$select.on('change', function () {
			refreshRelationshipPane($wrap);
		});
		$wrap.on('click', '.cf-relationship-selected__remove', function (e) {
			e.preventDefault();
			var value = String($(this).data('value'));
			$select
				.find('option')
				.filter(function () {
					return String($(this).val()) === value;
				})
				.prop('selected', false);
			$select.trigger('change');
		});

		refreshRelationshipPane($wrap);
	}

	function searchRelational($wrap, query) {
		if (!cfg.searchUrl || !cfg.restNonce) {
			return;
		}

		var type = $wrap.data('cf-type');
		var $select = $wrap.find('.cf-relational__select');
		var $status = $wrap.find('.cf-relational-search__status');
		var multiple = $select.prop('multiple');
		var include = String($wrap.data('cfInclude') || '');
		var endpoint = '';
		var params = {
			s: query,
			include: include,
			group_key: String($wrap.data('cfGroupKey') || ''),
			field_key: String($wrap.data('cfFieldKey') || ''),
		};

		if (type === 'post_object' || type === 'relationship' || type === 'page_link') {
			endpoint = 'posts';
			params.post_types = String($wrap.data('cfPostTypes') || '');
			params.post_statuses = String($wrap.data('cfPostStatuses') || '');
			params.taxonomy_terms = String($wrap.data('cfTaxonomyTerms') || '');
			if (type === 'relationship') {
				params.filter_post_type = String($wrap.find('.cf-relationship-filter--post-type').val() || '');
				params.filter_taxonomy = String($wrap.find('.cf-relationship-filter--taxonomy').val() || '');
			}
		} else if (type === 'term') {
			endpoint = 'terms';
			params.taxonomy = String($wrap.data('cfTaxonomy') || '');
		} else if (type === 'user') {
			endpoint = 'users';
		} else {
			return;
		}

		$wrap.addClass('is-loading');
		$status.text('Searching…');

		$.ajax({
			url: cfg.searchUrl + endpoint,
			data: params,
			beforeSend: function (xhr) {
				xhr.setRequestHeader('X-WP-Nonce', cfg.restNonce);
			},
		})
			.done(function (response) {
				var results = response.results || [];
				rebuildSelectOptions($select, results, multiple);
				$status.text(
					results.length
						? results.length + ' result' + (results.length === 1 ? '' : 's')
						: 'No matches found'
				);
			})
			.fail(function () {
				$status.text('Search failed. Try again.');
			})
			.always(function () {
				$wrap.removeClass('is-loading');
			});
	}

	function appendCreatedTermToSelect($wrap, term) {
		var $select = $wrap.find('.cf-relational__select');
		if (!$select.length || !term || !term.value) {
			return;
		}

		var value = String(term.value);
		var $option = $select.find('option').filter(function () {
			return String($(this).val()) === value;
		});
		if (!$option.length) {
			$option = $('<option>').val(value).text(term.label || value).appendTo($select);
		}

		if ($select.prop('multiple')) {
			$option.prop('selected', true);
		} else {
			$select.val(value);
		}
		$select.trigger('change');
	}

	function appendCreatedTermToParentSelect($wrap, term) {
		var $parent = $wrap.find('.cf-term-create__parent');
		if (!$parent.length || !term || !term.value) {
			return;
		}

		var value = String(term.value);
		var $option = $parent.find('option').filter(function () {
			return String($(this).val()) === value;
		});
		if (!$option.length) {
			$option = $('<option>').val(value).text(term.label || value).appendTo($parent);
		}
	}

	function appendCreatedTermToChoices($wrap, term) {
		var $choices = $wrap.find('.cf-choices').first();
		if (!$choices.length || !term || !term.value) {
			return;
		}

		var value = String(term.value);
		var $existing = $choices.find('input').filter(function () {
			return String($(this).val()) === value;
		});
		if ($existing.length) {
			$existing.prop('checked', true).trigger('change');
			return;
		}

		var inputType = String($wrap.data('cfInputType') || 'checkbox') === 'radio' ? 'radio' : 'checkbox';
		var inputName = String($wrap.data('cfInputName') || '');
		var id = 'cf-term-' + value + '-' + Math.random().toString(36).slice(2, 8);
		var $input = $('<input>')
			.attr('type', inputType)
			.attr('id', id)
			.attr('name', inputName)
			.val(value)
			.prop('checked', true);
		var $label = $('<label class="cf-choice">')
			.append($input)
			.append(' ')
			.append($('<span>').text(term.label || value));

		$choices.append($label);
		$input.trigger('change');
	}

	function createTerm($creator) {
		if (!cfg.createTermUrl || !cfg.restNonce) {
			return;
		}

		var $scope = $creator.closest('.cf-relational-search, .cf-term-choices');
		var $input = $creator.find('.cf-term-create__input');
		var $parent = $creator.find('.cf-term-create__parent');
		var $button = $creator.find('.cf-term-create__button');
		var $status = $creator.find('.cf-term-create__status');
		var termName = $.trim($input.val());
		if (!termName) {
			$status.text('Enter a term name.');
			$input.trigger('focus');
			return;
		}

		$button.prop('disabled', true);
		$status.text('Adding…');

		$.ajax({
			url: cfg.createTermUrl,
			method: 'POST',
			data: {
				group_key: String($scope.data('cfGroupKey') || ''),
				field_key: String($scope.data('cfFieldKey') || ''),
				term_name: termName,
				term_parent: $parent.length ? String($parent.val() || '0') : '0',
			},
			beforeSend: function (xhr) {
				xhr.setRequestHeader('X-WP-Nonce', cfg.restNonce);
			},
		})
			.done(function (response) {
				var term = response.term || null;
				if ($scope.is('.cf-relational-search')) {
					appendCreatedTermToSelect($scope, term);
				} else {
					appendCreatedTermToChoices($scope, term);
				}
				appendCreatedTermToParentSelect($scope, term);
				$input.val('');
				$parent.val('0');
				$status.text('Term added.');
			})
			.fail(function (xhr) {
				var message =
					xhr.responseJSON && xhr.responseJSON.message
						? xhr.responseJSON.message
						: 'Could not add term.';
				$status.text(message);
			})
			.always(function () {
				$button.prop('disabled', false);
			});
	}

	var searchTimers = {};

	function initRelationalSearch($wrap) {
		var $input = $wrap.find('.cf-relational-search__input');
		if (!$input.length) {
			return;
		}
		initRelationshipPane($wrap);
		if ($wrap.data('cfRelationalInit')) {
			return;
		}
		$wrap.data('cfRelationalInit', true);

		var key = $wrap.data('cf-type') + '-' + ($wrap.data('cfTaxonomy') || '') + '-' + $wrap.index();
		$input.on('input', function () {
			clearTimeout(searchTimers[key]);
			var query = $.trim($input.val());
			searchTimers[key] = setTimeout(function () {
				searchRelational($wrap, query);
			}, 300);
		});
		$wrap.on('change', '.cf-relationship-filter', function () {
			searchRelational($wrap, $.trim($input.val()));
		});
	}

	function initRelationalSearches(context) {
		$(context)
			.find('.cf-relational-search')
			.each(function () {
				initRelationalSearch($(this));
			});
	}

	function isLayoutCell(cell, type) {
		var $field = $(cell).children('.cf-field--layout[data-cf-layout-type]').first();
		if (!$field.length) {
			return false;
		}

		if (type && String($field.data('cfLayoutType')) !== type) {
			return false;
		}

		return $field;
	}

	function refreshLayoutRows($container) {
		$container.find('> .cf-meta-box__row').each(function () {
			var $row = $(this);
			if ($row.hasClass('cf-layout-tabs-row')) {
				return;
			}

			var $cells = $row.children('.cf-meta-box__cell');
			if (!$cells.length) {
				return;
			}

			var visibleCells = $cells.filter(function () {
				return !this.hidden;
			}).length;
			this.hidden = visibleCells === 0;
		});
	}

	function tabLabel($field) {
		return $.trim($field.find('.cf-layout-field__title').first().text()) || 'Tab';
	}

	function activateTabGroup(group, index) {
		group.tabs.forEach(function (tab, tabIndex) {
			var active = tabIndex === index;
			tab.button
				.toggleClass('is-active', active)
				.attr('aria-selected', active ? 'true' : 'false')
				.attr('tabindex', active ? '0' : '-1');
			tab.cells.forEach(function (cell) {
				var accordionSection = $(cell).data('cfAccordionSection');
				cell.hidden = !active || (accordionSection && !accordionSection.field.hasClass('is-open'));
			});
		});
		refreshLayoutRows(group.container);
	}

	function renderTabGroup($container, tabs) {
		if (!tabs.length) {
			return;
		}

		var group = {
			container: $container,
			tabs: tabs,
		};
		var $nav = $('<div class="cf-layout-tabs" role="tablist"></div>');
		var $firstRow = $(tabs[0].marker).closest('.cf-meta-box__row');
		var activeIndex = 0;

		tabs.forEach(function (tab, index) {
			var buttonId = 'cf-layout-tab-' + Math.random().toString(36).slice(2, 10);
			var selected = String(tab.field.data('selected')) === '1';
			if (selected) {
				activeIndex = index;
			}

			tab.button = $('<button type="button" class="cf-layout-tab-button"></button>')
				.attr({
					id: buttonId,
					role: 'tab',
					'aria-selected': 'false',
				})
				.text(tabLabel(tab.field))
				.on('click', function () {
					activateTabGroup(group, index);
				})
				.on('keydown', function (e) {
					var nextIndex = index;
					if (e.key === 'ArrowRight') {
						nextIndex = (index + 1) % tabs.length;
					} else if (e.key === 'ArrowLeft') {
						nextIndex = (index - 1 + tabs.length) % tabs.length;
					} else {
						return;
					}

					e.preventDefault();
					activateTabGroup(group, nextIndex);
					tabs[nextIndex].button.trigger('focus');
				});
			$nav.append(tab.button);
			tab.marker.hidden = true;
		});

		$firstRow.before(
			$('<div class="cf-meta-box__row cf-layout-tabs-row"></div>').append(
				$('<div class="cf-meta-box__cell cf-field--w100"></div>').append($nav)
			)
		);

		activateTabGroup(group, activeIndex);
	}

	function initTabLayouts($container) {
		var cells = $container.find('> .cf-meta-box__row > .cf-meta-box__cell').toArray();
		var tabs = [];
		var current = null;

		function flushTabs() {
			renderTabGroup($container, tabs);
			tabs = [];
			current = null;
		}

		cells.forEach(function (cell) {
			var $tab = isLayoutCell(cell, 'tab');
			var $layout = isLayoutCell(cell);

			if ($tab) {
				if (String($tab.data('endpoint')) === '1') {
					cell.hidden = true;
					flushTabs();
					return;
				}

				current = {
					marker: cell,
					field: $tab,
					cells: [],
					button: null,
				};
				tabs.push(current);
				return;
			}

			if (current && !($layout && String($layout.data('cfLayoutType')) === 'tab')) {
				current.cells.push(cell);
			}
		});

		flushTabs();
	}

	function setAccordionSection(section, open) {
		section.field.toggleClass('is-open', open).attr('aria-expanded', open ? 'true' : 'false');
		section.cells.forEach(function (cell) {
			cell.hidden = !open;
		});
	}

	function initAccordionLayouts($container) {
		var cells = $container.find('> .cf-meta-box__row > .cf-meta-box__cell').toArray();
		var sections = [];
		var current = null;

		cells.forEach(function (cell) {
			var $accordion = isLayoutCell(cell, 'accordion');
			var $layout = isLayoutCell(cell);

			if ($accordion) {
				if (String($accordion.data('endpoint')) === '1') {
					cell.hidden = true;
					current = null;
					return;
				}

				current = {
					marker: cell,
					field: $accordion,
					cells: [],
					multi: String($accordion.data('multiExpand')) === '1',
				};
				sections.push(current);
				return;
			}

			if (current && !($layout && String($layout.data('cfLayoutType')) === 'accordion')) {
				current.cells.push(cell);
			}
		});

		sections.forEach(function (section) {
			var $title = section.field.find('.cf-layout-field__title').first();
			var label = $.trim($title.text()) || 'Accordion';
			var open = String(section.field.data('open')) === '1';

			section.cells.forEach(function (cell) {
				$(cell).data('cfAccordionSection', section);
			});
			$title.empty().append($('<button type="button" class="cf-layout-accordion-button"></button>').text(label));
			section.field.attr('aria-expanded', open ? 'true' : 'false');
			setAccordionSection(section, open);

			section.field.find('.cf-layout-accordion-button').on('click', function () {
				var nextOpen = !section.field.hasClass('is-open');
				if (nextOpen && !section.multi) {
					sections.forEach(function (other) {
						if (other !== section) {
							setAccordionSection(other, false);
						}
					});
				}
				setAccordionSection(section, nextOpen);
				refreshLayoutRows($container);
			});
		});
	}

	function initLayoutControls(context) {
		$(context)
			.find('.cf-meta-box--grid, .cf-term-fields')
			.addBack('.cf-meta-box--grid, .cf-term-fields')
			.each(function () {
				var $container = $(this);
				if ($container.data('cfLayoutControlsInit')) {
					return;
				}

				$container.data('cfLayoutControlsInit', true);
				initAccordionLayouts($container);
				initTabLayouts($container);
				refreshLayoutRows($container);
			});
	}

	function refreshGoogleMapPreview($field) {
		var address = $.trim($field.find('[name$="[address]"]').val() || '');
		var lat = $.trim($field.find('[name$="[lat]"]').val() || '');
		var lng = $.trim($field.find('[name$="[lng]"]').val() || '');
		var zoom = parseInt($field.find('[name$="[zoom]"]').val(), 10);
		var query = lat && lng ? lat + ',' + lng : address;
		var $frame = $field.find('.cf-google-map-field__frame');
		var $open = $field.find('.cf-google-map-field__open');
		var emptyText = String($field.find('.cf-google-map-field__preview').data('emptyText') || '');

		if (!query) {
			$frame.html($('<p>').text(emptyText));
			$open.addClass('is-disabled').attr('href', '#');
			return;
		}

		if (!zoom || zoom < 1) {
			zoom = 14;
		}
		if (zoom > 22) {
			zoom = 22;
		}

		var encoded = window.encodeURIComponent(query);
		var embedUrl = 'https://maps.google.com/maps?q=' + encoded + '&z=' + zoom + '&output=embed';
		var openUrl = 'https://www.google.com/maps/search/?api=1&query=' + encoded;
		var $iframe = $frame.find('iframe');

		if (!$iframe.length) {
			$iframe = $('<iframe>')
				.attr({
					title: 'Map preview',
					loading: 'lazy',
					referrerpolicy: 'no-referrer-when-downgrade',
				})
				.appendTo($frame.empty());
		}

		$iframe.attr('src', embedUrl);
		$open.removeClass('is-disabled').attr('href', openUrl);
	}

	function parseGoogleMapCoordinates(value) {
		var text = $.trim(value || '');
		var decoded;
		var patterns;
		var i;
		var match;
		var lat;
		var lng;

		if (!text) {
			return null;
		}

		try {
			decoded = window.decodeURIComponent(text);
		} catch (e) {
			decoded = text;
		}

		patterns = [
			/@(-?\d+(?:\.\d+)?),\s*(-?\d+(?:\.\d+)?)/,
			/[?&](?:q|query|ll)=(-?\d+(?:\.\d+)?),\s*(-?\d+(?:\.\d+)?)/,
			/^\s*(-?\d+(?:\.\d+)?),\s*(-?\d+(?:\.\d+)?)\s*$/
		];

		for (i = 0; i < patterns.length; i++) {
			match = decoded.match(patterns[i]);
			if (!match) {
				continue;
			}
			lat = parseFloat(match[1]);
			lng = parseFloat(match[2]);
			if (!isNaN(lat) && !isNaN(lng) && lat >= -90 && lat <= 90 && lng >= -180 && lng <= 180) {
				return { lat: String(lat), lng: String(lng) };
			}
		}

		return null;
	}

	function syncGoogleMapAddressCoordinates($field) {
		var $address = $field.find('[name$="[address]"]').first();
		var coords = parseGoogleMapCoordinates($address.val());

		if (!coords) {
			return;
		}

		$field.find('[name$="[lat]"]').val(coords.lat).attr('value', coords.lat);
		$field.find('[name$="[lng]"]').val(coords.lng).attr('value', coords.lng);
	}

	function initGoogleMapFields(context) {
		$(context)
			.find('.cf-google-map-field')
			.addBack('.cf-google-map-field')
			.each(function () {
				var $field = $(this);
				if ($field.data('cfMapInit')) {
					return;
				}

				$field.data('cfMapInit', true);
				$field.on('click', '.cf-google-map-field__refresh', function (e) {
					e.preventDefault();
					refreshGoogleMapPreview($field);
				});
				$field.on('change', 'input', function () {
					if ($(this).is('[name$="[address]"]')) {
						syncGoogleMapAddressCoordinates($field);
					}
					refreshGoogleMapPreview($field);
				});
				$field.on('click', '.cf-google-map-field__open.is-disabled', function (e) {
					e.preventDefault();
				});
			});
	}

	$(function () {
		initColorPickers(document);
		initIconPickers(document);
		$('.cf-relational-search').each(function () {
			initRelationalSearch($(this));
		});
		initLayoutControls(document);
		initGoogleMapFields(document);

		if ($.fn.sortable) {
			$('.cf-gallery__preview').sortable({
				items: '.cf-gallery-preview__item',
				cursor: 'move',
				tolerance: 'pointer',
				update: function () {
					syncGalleryInput($(this).closest('.cf-gallery'));
				},
			});
		}

		$(document).on('click', '.cf-media__select', function (e) {
			e.preventDefault();
			openMediaFrame($(this).closest('.cf-media'));
		});

		$(document).on('click', '.cf-media__clear', function (e) {
			e.preventDefault();
			var $wrap = $(this).closest('.cf-media');
			$wrap.find('.cf-media__input').val('');
			$wrap.find('.cf-media__preview').empty();
			$(this).prop('hidden', true);
		});

		$(document).on('click', '.cf-gallery__add', function (e) {
			e.preventDefault();
			openGalleryFrame($(this).closest('.cf-gallery'));
		});

		$(document).on('click', '.cf-gallery__clear', function (e) {
			e.preventDefault();
			var $gallery = $(this).closest('.cf-gallery');
			$gallery.find('.cf-gallery__preview').empty();
			syncGalleryInput($gallery);
		});

		$(document).on('click', '.cf-gallery-preview__remove', function (e) {
			e.preventDefault();
			var $gallery = $(this).closest('.cf-gallery');
			$(this).closest('.cf-gallery-preview__item').remove();
			syncGalleryInput($gallery);
		});

		$(document).on('click', '.cf-term-create__button', function (e) {
			e.preventDefault();
			createTerm($(this).closest('.cf-term-create'));
		});

		$(document).on('keydown', '.cf-term-create__input', function (e) {
			if (e.key === 'Enter') {
				e.preventDefault();
				createTerm($(this).closest('.cf-term-create'));
			}
		});

		$(document).on('input change', '.cf-oembed-url', function () {
			var $input = $(this);
			clearTimeout($input.data('cfOembedTimer'));
			$input.data(
				'cfOembedTimer',
				setTimeout(function () {
					fetchOembed($input);
				}, 400)
			);
		});

		$('.cf-oembed-url').each(function () {
			if ($.trim($(this).val()) !== '') {
				fetchOembed($(this));
			}
		});

		$(document).on('change', '.cf-toggle input[type="checkbox"]', function () {
			var $input = $(this);
			var text = $input.is(':checked') ? $input.data('onText') : $input.data('offText');
			$input.closest('.cf-toggle').find('span').text(text || '');
		});

		$(document).on('change', '.cf-radio-other-toggle', function () {
			var $toggle = $(this);
			var $choices = $toggle.closest('.cf-choices');
			var target = String($toggle.data('target') || '');

			$choices.find('input[type="radio"][name]').prop('checked', false);
			$choices.find('.cf-radio-other-value').prop('disabled', true);
			if ($toggle.is(':checked') && target) {
				$('#' + target).prop('disabled', false).trigger('focus');
			}
		});

		$(document).on('change', '.cf-choices input[type="radio"][name]', function () {
			var $choices = $(this).closest('.cf-choices');
			$choices.find('.cf-radio-other-toggle').prop('checked', false);
			$choices.find('.cf-radio-other-value').prop('disabled', true);
		});

		$(document).on('change', '.cf-checkbox-toggle-all', function () {
			var $toggle = $(this);
			var targetName = String($toggle.data('target') || '');
			var $choices = $toggle.closest('.cf-choices');
			var checked = $toggle.is(':checked');
			$choices
				.find('input[type="checkbox"]')
				.not($toggle)
				.filter(function () {
					return !targetName || this.name === targetName;
				})
				.prop('checked', checked)
				.trigger('change');
		});

		$(document).on('change', '.cf-choices input[type="checkbox"]:not(.cf-checkbox-toggle-all)', function () {
			var $choices = $(this).closest('.cf-choices');
			var $toggle = $choices.find('.cf-checkbox-toggle-all');
			if (!$toggle.length) {
				return;
			}
			var targetName = String($toggle.data('target') || '');
			var $items = $choices.find('input[type="checkbox"]').not($toggle).filter(function () {
				return !targetName || this.name === targetName;
			});
			var checkedCount = $items.filter(':checked').length;
			$toggle.prop('checked', $items.length > 0 && checkedCount === $items.length);
			$toggle.prop('indeterminate', checkedCount > 0 && checkedCount < $items.length);
		});

		$(document).on('click', '.cf-checkbox-custom-add', function (e) {
			e.preventDefault();
			var $button = $(this);
			var name = String($button.data('name') || '');
			if (!name) {
				return;
			}
			var $row = $('<label class="cf-choice cf-choice--custom"></label>');
			$row.append($('<span></span>').text('Custom'));
			$row.append($('<input type="text" class="cf-checkbox-custom-value">').attr('name', name));
			$row.append($('<button type="button" class="button-link cf-checkbox-custom-remove" aria-label="Remove custom value">&times;</button>'));
			$row.insertBefore($button);
			$row.find('input').trigger('focus');
		});

		$(document).on('click', '.cf-checkbox-custom-remove', function (e) {
			e.preventDefault();
			$(this).closest('.cf-choice--custom').remove();
		});

		$(document).on('input', '.cf-range-field', function () {
			var $output = $(this).siblings('.cf-range-value');
			if ($output.length) {
				$output.text($(this).val());
			}
		});

		$(document).on('change', '.cf-icon-picker__select', function () {
			setIconPickerValue($(this).closest('.cf-icon-picker'), $(this).val() || '');
		});

		$(document).on('click', '.cf-icon-picker__tab', function (e) {
			e.preventDefault();
			activateIconPickerTab($(this).closest('.cf-icon-picker'), $(this).data('iconTab'));
		});

		$(document).on('input change', '.cf-icon-picker__url', function () {
			setIconPickerValue($(this).closest('.cf-icon-picker'), $(this).val() || '');
		});

		$(document).on('click', '.cf-icon-picker__media', function (e) {
			e.preventDefault();
			if (typeof wp === 'undefined' || !wp.media) {
				return;
			}
			var $picker = $(this).closest('.cf-icon-picker');
			var frame = wp.media({
				title: 'Select Icon',
				library: { type: 'image' },
				multiple: false,
			});

			frame.on('select', function () {
				var attachment = frame.state().get('selection').first().toJSON();
				var url = attachment.url || '';
				$picker.find('.cf-icon-picker__url').val(url);
				setIconPickerValue($picker, url);
			});

			frame.open();
		});

		revealBlockEditorMetaBoxes();
		if (window.MutationObserver) {
			var observer = new MutationObserver(revealBlockEditorMetaBoxes);
			observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['hidden'] });
		}
	});

	window.wppsCfInitColorPickers = initColorPickers;
	window.wppsCfInitRelationalSearches = initRelationalSearches;
	window.wppsCfInitLayoutControls = initLayoutControls;
})(jQuery);
