/* global tinymce, rsmRichTextEditor */
/* RSM rich text TinyMCE plugin v1.5.9 (no formatter.register during init) */
(function () {
	'use strict';

	var syncLock = false;

	function parseProfilesParam(raw) {
		if (!raw) {
			return null;
		}

		if (typeof raw === 'object') {
			return raw;
		}

		if (typeof raw === 'string') {
			try {
				return JSON.parse(raw);
			} catch (error) {
				return null;
			}
		}

		return null;
	}

	function profilesFor(editor) {
		var fromParam = parseProfilesParam(editor.getParam('rsm_rt_profiles'));
		if (fromParam) {
			return fromParam;
		}

		if (typeof rsmRichTextEditor !== 'undefined' && rsmRichTextEditor.profiles) {
			return rsmRichTextEditor.profiles;
		}

		return null;
	}

	function configFor(editor) {
		var profile = editor.getParam('rsm_rt_profile') || 'headline';
		var profiles = profilesFor(editor);

		if (!profiles) {
			return {
				profile: profile,
				sizes: [],
				colors: [],
				fonts: [],
				listStyles: [],
				markerColorClasses: [],
				markerColorMap: {},
				sizeClasses: [],
				colorClasses: [],
				effectClasses: [],
				sizeLimits: { min: 8, max: 120 },
				i18n: {},
			};
		}

		return profiles[profile] || profiles.headline;
	}

	function classNamesFromConfig(items) {
		return (items || []).map(function (item) {
			return item.class;
		});
	}

	function labelForClass(items, className) {
		var match = (items || []).filter(function (item) {
			return item.class === className;
		});

		return match.length ? match[0].label : '';
	}

	function labelForHex(items, hex) {
		hex = normalizeHex(hex);
		if (!hex) {
			return '';
		}

		var match = (items || []).filter(function (item) {
			return normalizeHex(item.hex || '') === hex;
		});

		return match.length ? colorMenuLabel(match[0]) : hex;
	}

	function sizeLimits(config) {
		return config.sizeLimits || { min: 8, max: 120 };
	}

	function normalizeHex(value) {
		var hex = String(value || '').trim().toLowerCase();
		if (!hex) {
			return '';
		}
		if (hex.charAt(0) !== '#') {
			hex = '#' + hex;
		}
		if (!/^#([0-9a-f]{3}|[0-9a-f]{6})$/.test(hex)) {
			return '';
		}
		if (hex.length === 4) {
			hex =
				'#' +
				hex.charAt(1) +
				hex.charAt(1) +
				hex.charAt(2) +
				hex.charAt(2) +
				hex.charAt(3) +
				hex.charAt(3);
		}
		return hex;
	}

	function parseStyleMap(styleValue) {
		var map = {};
		String(styleValue || '')
			.split(';')
			.forEach(function (part) {
				part = part.trim();
				if (!part || part.indexOf(':') === -1) {
					return;
				}
				var pieces = part.split(':');
				var prop = pieces.shift().trim().toLowerCase();
				var value = pieces.join(':').trim();
				if (prop) {
					map[prop] = value;
				}
			});
		return map;
	}

	function buildStyleString(styleMap) {
		return Object.keys(styleMap)
			.map(function (key) {
				return key + ':' + styleMap[key];
			})
			.join(';');
	}

	function removeStyleProperties(styleValue, properties) {
		var map = parseStyleMap(styleValue);
		(properties || []).forEach(function (prop) {
			delete map[prop];
		});
		return buildStyleString(map);
	}

	function spanHasGroupClass(span, groupClasses) {
		var classes = (span.getAttribute('class') || '').split(/\s+/);
		for (var i = 0; i < groupClasses.length; i++) {
			if (classes.indexOf(groupClasses[i]) !== -1) {
				return true;
			}
		}
		return false;
	}

	function tokenSpanFor(editor, groupClasses) {
		return editor.dom.getParent(editor.selection.getNode(), function (candidate) {
			return (
				candidate &&
				candidate.nodeName &&
				candidate.nodeName.toLowerCase() === 'span' &&
				(spanHasGroupClass(candidate, groupClasses) ||
					(candidate.getAttribute('style') || '').replace(/\s+/g, '') !== '')
			);
		});
	}

	function activeClassOnNode(editor, node, classNames) {
		var span = editor.dom.getParent(node, function (candidate) {
			return (
				candidate &&
				candidate.nodeName &&
				candidate.nodeName.toLowerCase() === 'span' &&
				spanHasGroupClass(candidate, classNames)
			);
		});

		if (!span) {
			return '';
		}

		var current = (span.getAttribute('class') || '').split(/\s+/);
		for (var j = 0; j < classNames.length; j++) {
			if (current.indexOf(classNames[j]) !== -1) {
				return classNames[j];
			}
		}

		return '';
	}

	function activeInlineFontSizePx(editor) {
		var span = editor.dom.getParent(editor.selection.getNode(), 'span[style]');
		if (!span) {
			return 0;
		}

		var styleMap = parseStyleMap(span.getAttribute('style'));
		var match = /^(\d{1,3})px$/.exec(styleMap['font-size'] || '');
		return match ? parseInt(match[1], 10) : 0;
	}

	function activeInlineColorHex(editor) {
		var span = editor.dom.getParent(editor.selection.getNode(), 'span[style]');
		if (!span) {
			return '';
		}

		return normalizeHex(parseStyleMap(span.getAttribute('style')).color || '');
	}

	function activeInlineBackgroundColorHex(editor) {
		var span = editor.dom.getParent(editor.selection.getNode(), 'span[style]');
		if (!span) {
			return '';
		}

		return normalizeHex(parseStyleMap(span.getAttribute('style'))['background-color'] || '');
	}

	function activeInlineFontFamily(editor) {
		var span = editor.dom.getParent(editor.selection.getNode(), 'span[style]');
		if (!span) {
			return '';
		}

		return parseStyleMap(span.getAttribute('style'))['font-family'] || '';
	}

	function sanitizeFontFamilyStack(value) {
		var parts = String(value || '')
			.split(',')
			.map(function (part) {
				return part.trim().replace(/^['"]|['"]$/g, '');
			})
			.filter(function (part) {
				return part && /^[-a-z0-9 ]+$/i.test(part);
			});

		return parts.join(', ');
	}

	function unwrapNode(node) {
		var parent = node.parentNode;
		if (!parent) {
			return;
		}

		while (node.firstChild) {
			parent.insertBefore(node.firstChild, node);
		}

		parent.removeChild(node);
	}

	function cleanHtmlForGroup(editor, html, groupClasses, styleProperties) {
		if (!html) {
			return html;
		}

		var doc = editor.getDoc();
		var container = doc.createElement('div');
		container.innerHTML = html;

		var spans = container.getElementsByTagName('span');
		var i;

		for (i = spans.length - 1; i >= 0; i--) {
			var span = spans[i];
			var j;

			for (j = 0; j < groupClasses.length; j++) {
				editor.dom.removeClass(span, groupClasses[j]);
			}

			if (styleProperties && styleProperties.length) {
				var nextStyle = removeStyleProperties(span.getAttribute('style') || '', styleProperties);
				if (nextStyle) {
					span.setAttribute('style', nextStyle);
				} else {
					span.removeAttribute('style');
				}
			}

			if (!(span.getAttribute('class') || '').replace(/\s+/g, '') && !(span.getAttribute('style') || '').replace(/\s+/g, '')) {
				unwrapNode(span);
			}
		}

		return container.innerHTML;
	}

	function ensureSelectionHtml(editor) {
		var selection = editor.selection;

		if (!selection.isCollapsed()) {
			return selection.getContent({ format: 'html' });
		}

		var node = selection.getNode();
		if (node && node.nodeType === 3) {
			selection.select(node);
			return selection.getContent({ format: 'html' });
		}

		if (node && editor.dom.isBlock(node)) {
			var textNode = node.firstChild;
			while (textNode && textNode.nodeType !== 3) {
				textNode = textNode.firstChild;
			}
			if (textNode) {
				selection.select(textNode);
				return selection.getContent({ format: 'html' });
			}
		}

		return selection.getContent({ format: 'html' });
	}

	function wrapSelectionHtml(editor, html, attributes) {
		var attrString = Object.keys(attributes)
			.map(function (key) {
				return key + '="' + attributes[key] + '"';
			})
			.join(' ');

		editor.selection.setContent('<span ' + attrString + '>' + html + '</span>', {
			format: 'html',
		});
	}

	function applyTokenClass(editor, className, groupClasses, styleProperty) {
		if (!className) {
			return;
		}

		syncLock = true;

		editor.undoManager.transact(function () {
			var tokenSpan = tokenSpanFor(editor, groupClasses);

			if (tokenSpan && editor.selection.isCollapsed()) {
				groupClasses.forEach(function (token) {
					editor.dom.removeClass(tokenSpan, token);
				});
				if (styleProperty) {
					var cleaned = removeStyleProperties(tokenSpan.getAttribute('style') || '', [styleProperty]);
					if (cleaned) {
						tokenSpan.setAttribute('style', cleaned);
					} else {
						tokenSpan.removeAttribute('style');
					}
				}
				editor.dom.addClass(tokenSpan, className);
				return;
			}

			var html = ensureSelectionHtml(editor);
			if (!html) {
				return;
			}

			html = cleanHtmlForGroup(editor, html, groupClasses, styleProperty ? [styleProperty] : []);
			wrapSelectionHtml(editor, html, { class: className });
		});

		editor.focus();
		editor.nodeChanged();
		syncLock = false;
	}

	function applyCustomFontSize(editor, px, sizeClasses, limits) {
		px = parseInt(px, 10);
		if (!px || px < limits.min || px > limits.max) {
			return;
		}

		syncLock = true;

		editor.undoManager.transact(function () {
			var html = ensureSelectionHtml(editor);
			if (!html) {
				return;
			}

			html = cleanHtmlForGroup(editor, html, sizeClasses, ['font-size']);
			wrapSelectionHtml(editor, html, { style: 'font-size:' + px + 'px' });
		});

		editor.focus();
		editor.nodeChanged();
		syncLock = false;
	}

	function applyCustomColor(editor, hex, colorClasses) {
		hex = normalizeHex(hex);
		if (!hex) {
			return;
		}

		syncLock = true;

		editor.undoManager.transact(function () {
			var html = ensureSelectionHtml(editor);
			if (!html) {
				return;
			}

			html = cleanHtmlForGroup(editor, html, colorClasses, ['color']);
			wrapSelectionHtml(editor, html, { style: 'color:' + hex });
		});

		editor.focus();
		editor.nodeChanged();
		syncLock = false;
	}

	function applyCustomHighlight(editor, hex, colorClasses) {
		hex = normalizeHex(hex);
		if (!hex) {
			return;
		}

		syncLock = true;

		editor.undoManager.transact(function () {
			var html = ensureSelectionHtml(editor);
			if (!html) {
				return;
			}

			html = cleanHtmlForGroup(editor, html, colorClasses, ['background-color']);
			wrapSelectionHtml(editor, html, { style: 'background-color:' + hex });
		});

		editor.focus();
		editor.nodeChanged();
		syncLock = false;
	}

	function applyCustomFontFamily(editor, family, colorClasses) {
		family = sanitizeFontFamilyStack(family);
		if (!family) {
			return;
		}

		syncLock = true;

		editor.undoManager.transact(function () {
			var html = ensureSelectionHtml(editor);
			if (!html) {
				return;
			}

			html = cleanHtmlForGroup(editor, html, colorClasses, ['font-family']);
			wrapSelectionHtml(editor, html, { style: 'font-family:' + family });
		});

		editor.focus();
		editor.nodeChanged();
		syncLock = false;
	}

	function colorMenuLabel(item) {
		var name = String(item.name || '').trim();
		var hex = String(item.hex || '').trim();

		if (name && hex) {
			return name + ' · ' + hex;
		}

		return item.label || hex || name;
	}

	function buildColorMenuItems(items, onSelect) {
		return (items || [])
			.map(function (item) {
				var label = colorMenuLabel(item);
				if (!label) {
					return null;
				}

				var menuItem = {
					text: label,
					onclick: function () {
						onSelect(item);
					},
				};

				if (item.hex) {
					menuItem.classes = 'rsmspark-rt-color-item rsmspark-rt-hex-swatch';
					menuItem.onPostRender = function () {
						var root = this.getEl ? this.getEl() : null;
						if (!root) {
							return;
						}

						root.style.setProperty('--rsm-swatch', item.hex);
						var text = root.querySelector('.mce-text');
						if (text) {
							text.style.color = '#1d2327';
							text.style.fontWeight = '600';
						}
					};
				} else if (item.class) {
					var slug = item.class.replace('rsmspark-rt-color-', '');
					menuItem.classes = 'rsmspark-rt-color-item rsmspark-rt-color-item--' + slug;
				}

				return menuItem;
			})
			.filter(function (menuItem) {
				return !!menuItem;
			});
	}

	function buildSizeMenuItems(items, onSelect) {
		return (items || []).map(function (item) {
			return {
				text: item.label,
				classes: 'rsmspark-rt-size-item',
				onclick: function () {
					onSelect(item);
				},
				onPostRender: function () {
					if (!item.px) {
						return;
					}

					var root = this.getEl ? this.getEl() : null;
					if (!root) {
						return;
					}

					var text = root.querySelector('.mce-text');
					if (text) {
						text.style.fontSize = Math.min(parseInt(item.px, 10), 22) + 'px';
						text.style.fontWeight = '600';
						text.style.lineHeight = '1.2';
					}
				},
			};
		});
	}

	function openCustomHighlightDialog(editor, config, colorClasses) {
		var current = activeInlineBackgroundColorHex(editor);

		editor.windowManager.open({
			title: config.i18n.customHighlightTitle || 'Custom highlight color',
			body: [
				{
					type: 'textbox',
					name: 'hex',
					label: config.i18n.customColorLabel || 'Hex color',
					value: current || '#fef08a',
				},
				{
					type: 'label',
					text: config.i18n.customColorHelp || 'Example: #7c3aed or 7c3aed',
				},
			],
			onsubmit: function (event) {
				var hex = normalizeHex(event.data.hex);
				if (!hex) {
					editor.windowManager.alert(
						config.i18n.customColorInvalid || 'Enter a valid hex color (#RGB or #RRGGBB).'
					);
					return;
				}
				applyCustomHighlight(editor, hex, colorClasses);
			},
		});
	}

	function openCustomFontDialog(editor, config, colorClasses) {
		var current = activeInlineFontFamily(editor);

		editor.windowManager.open({
			title: config.i18n.customFontTitle || 'Custom font family',
			body: [
				{
					type: 'textbox',
					name: 'family',
					label: config.i18n.customFontLabel || 'Font stack',
					value: current || '',
				},
				{
					type: 'label',
					text:
						config.i18n.customFontHelp ||
						'Example: Georgia, serif or Inter, system-ui, sans-serif',
				},
			],
			onsubmit: function (event) {
				var family = sanitizeFontFamilyStack(event.data.family);
				if (!family) {
					editor.windowManager.alert(
						config.i18n.customFontInvalid || 'Enter a valid font-family stack.'
					);
					return;
				}
				applyCustomFontFamily(editor, family, colorClasses);
			},
		});
	}

	function openCustomSizeDialog(editor, config, sizeClasses) {
		var limits = sizeLimits(config);
		var current = activeInlineFontSizePx(editor);

		editor.windowManager.open({
			title: config.i18n.customSizeTitle || 'Custom font size',
			body: [
				{
					type: 'textbox',
					name: 'px',
					label: config.i18n.customSizeLabel || 'Size (px)',
					value: current ? String(current) : '16',
				},
				{
					type: 'label',
					text:
						config.i18n.customSizeHelp ||
						'Enter ' + limits.min + '–' + limits.max + '. Saved as inline font-size.',
				},
			],
			onsubmit: function (event) {
				var px = parseInt(event.data.px, 10);
				if (!px || px < limits.min || px > limits.max) {
					editor.windowManager.alert(
						config.i18n.customSizeInvalid ||
							'Enter a whole number between ' + limits.min + ' and ' + limits.max + '.'
					);
					return;
				}
				applyCustomFontSize(editor, px, sizeClasses, limits);
			},
		});
	}

	function openCustomHexDialog(editor, config, currentHex, applyHex) {
		editor.windowManager.open({
			title: config.i18n.customColorTitle || 'Custom color',
			body: [
				{
					type: 'textbox',
					name: 'hex',
					label: config.i18n.customColorLabel || 'Hex color',
					value: currentHex || '#2563eb',
				},
				{
					type: 'label',
					text: config.i18n.customColorHelp || 'Example: #7c3aed or 7c3aed',
				},
			],
			onsubmit: function (event) {
				var hex = normalizeHex(event.data.hex);
				if (!hex) {
					editor.windowManager.alert(
						config.i18n.customColorInvalid || 'Enter a valid hex color (#RGB or #RRGGBB).'
					);
					return;
				}
				applyHex(hex);
			},
		});
	}

	function openCustomColorDialog(editor, config, colorClasses) {
		openCustomHexDialog(editor, config, activeInlineColorHex(editor), function (hex) {
			applyCustomColor(editor, hex, colorClasses);
		});
	}

	function registerProfileColorMenu(
		editor,
		buttonName,
		tooltip,
		config,
		colorClasses,
		applyColorItem,
		activeLabelResolver,
		getCurrentHex
	) {
		var i18n = config.i18n || {};

		registerTokenMenu(
			editor,
			buttonName,
			tooltip,
			config.colors,
			colorClasses,
			function () {},
			function (items) {
				var menu = buildColorMenuItems(items, applyColorItem);

				menu.push({ text: '-' });
				menu.push({
					text: i18n.customColor || 'Custom color…',
					onclick: function () {
						openCustomHexDialog(
							editor,
							config,
							typeof getCurrentHex === 'function' ? getCurrentHex() : '',
							function (hex) {
								applyColorItem({ hex: hex });
							}
						);
					},
				});

				return menu;
			},
			activeLabelResolver
		);
	}

	function editorUiConfig() {
		if ( typeof rsmRichTextEditor !== 'undefined' && rsmRichTextEditor.editorUi ) {
			return rsmRichTextEditor.editorUi;
		}

		return {
			mediaRow: false,
			useRsmMedia: true,
		};
	}

	function toolbarIconFor(name) {
		// Use only core TinyMCE icons; custom icon ids break WP TinyMCE 4 init.
		var icons = {
			rsm_rt_color: 'forecolor',
			rsm_rt_highlight: 'backcolor',
			rsm_rt_list: 'bullist',
		};

		return icons[name] || false;
	}

	function toolbarTextFor(name, placeholder) {
		var short = {
			rsm_rt_size: 'Size',
			rsm_rt_font: 'Font',
			rsm_rt_list_color: 'Marker',
			rsm_rt_section: 'Section',
			rsm_rt_media: 'Image',
		};

		return short[name] || placeholder;
	}

	var TOOLBAR_ICON_SVGS = {
		'rsmspark-rt-size':
			"url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 18 18'%3E%3Cpath fill='%231d2327' d='M5 4h8v1.5H5V4zm0 8.5h8V14H5v-1.5zM9 6.5l2 2H7l2-2zm0 3l-2 2h4l-2-2z'/%3E%3C/svg%3E\")",
		'rsmspark-rt-font':
			"url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 18 18'%3E%3Ctext x='2' y='13' font-family='Georgia,serif' font-size='11' fill='%231d2327'%3EA%3C/text%3E%3Ctext x='10' y='13' font-family='Georgia,serif' font-size='8' fill='%231d2327'%3Ea%3C/text%3E%3C/svg%3E\")",
		'rsmspark-rt-list-color':
			"url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 18 18'%3E%3Ccircle cx='4' cy='5' r='2' fill='%23ff6b35'/%3E%3Crect x='8' y='4' width='8' height='2' fill='%231d2327'/%3E%3Ccircle cx='4' cy='9' r='2' fill='%23cbd5e1'/%3E%3Crect x='8' y='8' width='8' height='2' fill='%231d2327'/%3E%3Ccircle cx='4' cy='13' r='2' fill='%23cbd5e1'/%3E%3Crect x='8' y='12' width='8' height='2' fill='%231d2327'/%3E%3C/svg%3E\")",
		'rsmspark-rt-section':
			"url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 18 18'%3E%3Cpath fill='%231d2327' d='M3 4h12v2.5H3V4zm0 5h8v2.5H3V9z'/%3E%3C/svg%3E\")",
		'rsmspark-rt-media':
			"url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 18 18'%3E%3Crect x='2' y='4' width='14' height='10' rx='1.5' fill='none' stroke='%23ff6b35' stroke-width='1.5'/%3E%3Ccircle cx='6.5' cy='8' r='1.5' fill='%23ff6b35'/%3E%3Cpath fill='%23ff6b35' d='M3 13l3.5-3.5 2.5 2.5L11 8l4 5H3z'/%3E%3C/svg%3E\")",
	};

	function paintToolbarIcon(control, iconName) {
		var svg = TOOLBAR_ICON_SVGS[iconName];

		if (!svg || !control || typeof control.getEl !== 'function') {
			return;
		}

		window.setTimeout(function () {
			var root = control.getEl();
			if (!root) {
				return;
			}

			var ico = root.querySelector('.mce-ico');
			if (!ico) {
				return;
			}

			ico.style.backgroundImage = svg;
			ico.style.backgroundRepeat = 'no-repeat';
			ico.style.backgroundPosition = 'center';
			ico.style.backgroundSize = '18px 18px';
			ico.style.width = '18px';
			ico.style.height = '18px';
			ico.classList.add('rsmspark-rt-painted-icon');
		}, 0);
	}

	function mediaHostWindow() {
		var candidates = [window];

		try {
			if (window.top && window.top !== window) {
				candidates.unshift(window.top);
			}
			if (window.parent && window.parent !== window) {
				candidates.push(window.parent);
			}
		} catch (error) {
			/* Cross-origin frame — ignore. */
		}

		for (var i = 0; i < candidates.length; i++) {
			if (
				candidates[i] &&
				typeof candidates[i].rsmMediaOpenFrame === 'function'
			) {
				return candidates[i];
			}
		}

		return window;
	}

	function whenMediaPickerReady(callback, attempts) {
		var tries = attempts || 0;
		var host = mediaHostWindow();

		if (typeof host.rsmMediaOpenFrame === 'function') {
			callback(true);
			return;
		}

		if (typeof wp !== 'undefined' && wp.media) {
			callback(false);
			return;
		}

		if (tries < 40) {
			window.setTimeout(function () {
				whenMediaPickerReady(callback, tries + 1);
			}, 100);
			return;
		}

		callback(null);
	}

	function openEditorMedia(editor, config) {
		var editorUi = editorUiConfig();
		var mediaOpts = {
			title: config.i18n.insertMedia || 'Insert image',
			button: { text: 'Insert into content' },
			library: { type: 'image' },
			multiple: false,
		};

		function insertAttachment(attachment) {
			if (!attachment || !attachment.url) {
				return;
			}

			var alt = attachment.alt ? String(attachment.alt) : '';
			var html =
				'<img src="' +
				attachment.url +
				'" alt="' +
				alt.replace(/"/g, '&quot;') +
				'" class="alignnone size-medium" />';

			editor.focus();
			editor.insertContent(html);
			editor.fire('change');

			if (window.jQuery) {
				window.jQuery(document).trigger('rsmspark-rt-editor-content-changed', [
					editor.id,
					editor.getContent(),
				]);
			}
		}

		function openPicker(useRsm) {
			var host = mediaHostWindow();

			if (useRsm && typeof host.rsmMediaOpenFrame === 'function') {
				host.rsmMediaOpenFrame(mediaOpts, function (attachments) {
					if (attachments && attachments[0]) {
						insertAttachment(attachments[0]);
					}
				});
				return;
			}

			var wpHost = host.wp ? host : window;
			if (typeof wpHost.wp !== 'undefined' && wpHost.wp.media) {
				var frame = wpHost.wp.media(mediaOpts);
				frame.on('select', function () {
					var model = frame.state().get('selection').first();
					if (model) {
						insertAttachment(model.toJSON());
					}
				});
				frame.open();
				return;
			}

			if (editor.windowManager && editor.windowManager.alert) {
				editor.windowManager.alert(
					'Image picker unavailable. Enable RSM Media and reload the editor.'
				);
			}
		}

		whenMediaPickerReady(function (useRsm) {
			if (useRsm === null) {
				if (editor.windowManager && editor.windowManager.alert) {
					editor.windowManager.alert(
						'RSM Media picker did not load. Check that RSM Media is active, then reload the page.'
					);
				}
				return;
			}

			var preferRsm = useRsm && editorUi.useRsmMedia !== false;
			openPicker(preferRsm);
		});
	}

	function safeAddButton(editor, name, buttonConfig) {
		try {
			editor.addButton(name, buttonConfig);
		} catch (error) {
			if (typeof console !== 'undefined' && console.error) {
				console.error('[RSM rich text] Toolbar button failed:', name, error);
			}
		}
	}

	function registerMediaButton(editor, config) {
		var i18n = config.i18n || {};

		safeAddButton(editor, 'rsm_rt_media', {
			text: toolbarTextFor('rsm_rt_media', i18n.insertMedia || 'Image'),
			tooltip: i18n.insertMedia || 'Insert image (RSM Media)',
			onclick: function () {
				openEditorMedia(editor, config);
			},
		});
	}

	function registerTokenMenu(editor, name, placeholder, items, groupClasses, onApply, menuItemBuilder, displayResolver) {
		var menuItems;

		if (typeof menuItemBuilder === 'function') {
			menuItems = menuItemBuilder(items, onApply);
		} else {
			menuItems = (items || []).map(function (item) {
				return {
					text: item.label,
					onclick: function () {
						onApply(item.class);
					},
				};
			});
		}

		var icon = toolbarIconFor(name);
		var buttonConfig = {
			type: 'menubutton',
			tooltip: placeholder,
			menu: menuItems,
			onPostRender: function () {
				var control = this;

				editor.on('nodeChange', function (event) {
					if (syncLock) {
						return;
					}

					var detail = '';
					if (typeof displayResolver === 'function') {
						detail = displayResolver(event.element) || '';
					} else {
						var active = activeClassOnNode(editor, event.element, groupClasses);
						detail = active ? labelForClass(items, active) : '';
					}

					var tip = detail ? placeholder + ': ' + detail : placeholder;
					if (typeof control.title === 'function') {
						control.title(tip);
					}
					if (typeof control.active === 'function') {
						control.active(!!detail);
					}
				});
			},
		};

		if (icon) {
			buttonConfig.icon = icon;
			buttonConfig.text = false;
		} else {
			buttonConfig.text = toolbarTextFor(name, placeholder);
		}

		safeAddButton(editor, name, buttonConfig);
	}

	function findList(editor) {
		return editor.dom.getParent(editor.selection.getNode(), 'ul,ol');
	}

	function ensureListFromSelection(editor) {
		var list = findList(editor);
		if (list) {
			return list;
		}

		var selectedBlocks = [];
		if (editor.selection && typeof editor.selection.getSelectedBlocks === 'function') {
			selectedBlocks = editor.selection.getSelectedBlocks() || [];
		}

		if (selectedBlocks.length > 1) {
			var items = selectedBlocks
				.map(function (block) {
					var html = block.innerHTML || editor.dom.encode(block.textContent || '');
					return html ? '<li>' + html + '</li>' : '';
				})
				.filter(function (item) {
					return !!item;
				})
				.join('');

			if (items) {
				editor.selection.setContent('<ul class="rsmspark-rt-list">' + items + '</ul>');
				list = findList(editor);
				if (list) {
					return list;
				}
			}
		}

		editor.execCommand('InsertUnorderedList');
		list = findList(editor);
		if (list && !editor.dom.hasClass(list, 'rsmspark-rt-list')) {
			editor.dom.addClass(list, 'rsmspark-rt-list');
		}

		return list;
	}

	function syncListClassAttribute(editor, list, config, styleClass, markerClass) {
		var classes = ['rsmspark-rt-list'];

		if (styleClass !== null) {
			if (styleClass) {
				classes.push(styleClass);
			}
		} else {
			listStyleClassNames(config).forEach(function (item) {
				if (item && editor.dom.hasClass(list, item)) {
					classes.push(item);
				}
			});
		}

		if (markerClass !== null) {
			if (markerClass) {
				classes.push(markerClass);
			}
		} else {
			listMarkerClassNames(config).forEach(function (item) {
				if (item && editor.dom.hasClass(list, item)) {
					classes.push(item);
				}
			});
		}

		var classValue = classes.join(' ');
		editor.dom.setAttrib(list, 'class', classValue);
		list.className = classValue;
	}

	function rebuildStyledList(editor, list, config, styleClass, markerClass) {
		if (!list) {
			return null;
		}

		syncListClassAttribute(editor, list, config, styleClass, markerClass);

		var tag = list.nodeName.toLowerCase();
		var classValue = list.getAttribute('class') || 'rsmspark-rt-list';
		var styleValue = list.getAttribute('style') || '';
		var items = [];
		var child = list.firstChild;

		while (child) {
			if (child.nodeType === 1 && child.nodeName.toLowerCase() === 'li') {
				items.push(child);
			}
			child = child.nextSibling;
		}

		var html = items
			.map(function (li) {
				return '<li>' + li.innerHTML + '</li>';
			})
			.join('');
		var rebuilt =
			'<' +
			tag +
			' class="' +
			classValue +
			'"' +
			(styleValue ? ' style="' + styleValue.replace(/"/g, '&quot;') + '"' : '') +
			'>' +
			html +
			'</' +
			tag +
			'>';

		var bookmark = editor.selection.getBookmark(2, true);
		editor.dom.setOuterHTML(list, rebuilt);
		if (bookmark) {
			editor.selection.moveToBookmark(bookmark);
		}

		return findList(editor);
	}

	function syncEditorTextarea(editor) {
		if (typeof editor.save === 'function') {
			editor.save();
		}
	}

	function listMarkerClassNames(config) {
		if (config.markerColorClasses && config.markerColorClasses.length) {
			return config.markerColorClasses;
		}

		return Object.keys(config.markerColorMap || {});
	}

	function markerHexFromItem(item, config) {
		var hex = normalizeHex(item.hex || '');
		if (hex) {
			return hex;
		}

		if (item.class && config.markerColorMap && config.markerColorMap[item.class]) {
			return normalizeHex(config.markerColorMap[item.class]);
		}

		var match = (config.colors || []).filter(function (colorItem) {
			return colorItem.class === item.class;
		});

		if (match.length && match[0].hex) {
			return normalizeHex(match[0].hex);
		}

		return '';
	}

	function applyListMarkerColorItem(editor, item) {
		var hex = markerHexFromItem(item, configFor(editor));
		if (hex) {
			applyListMarkerHex(editor, hex);
		}
	}

	function listStyleClassNames(config) {
		return (config.listStyles || []).map(function (item) {
			return item.class;
		});
	}

	function activeListMarkerClass(editor, config) {
		var list = findList(editor);
		if (!list) {
			return '';
		}

		var markerClasses = listMarkerClassNames(config);
		var current = (list.getAttribute('class') || '').split(/\s+/);

		for (var i = 0; i < markerClasses.length; i++) {
			if (current.indexOf(markerClasses[i]) !== -1) {
				return markerClasses[i];
			}
		}

		return '';
	}

	function activeListMarkerHex(editor) {
		var list = findList(editor);
		if (!list) {
			return '';
		}

		var styleMap = parseStyleMap(list.getAttribute('style'));
		return normalizeHex(styleMap['--rsmspark-rt-list-marker-color'] || '');
	}

	function clearListMarkerInlineStyle(editor, list) {
		if (!list) {
			return;
		}

		var style = removeStyleProperties(list.getAttribute('style'), ['--rsmspark-rt-list-marker-color']);
		if (style) {
			editor.dom.setAttrib(list, 'style', style);
		} else {
			editor.dom.setAttrib(list, 'style', null);
		}
	}

	function applyListMarkerHex(editor, hex) {
		hex = normalizeHex(hex);
		if (!hex) {
			return;
		}

		var config = configFor(editor);

		syncLock = true;
		editor.undoManager.transact(function () {
			var list = ensureListFromSelection(editor);
			if (!list) {
				return;
			}

			listMarkerClassNames(config).forEach(function (item) {
				editor.dom.removeClass(list, item);
			});

			syncListClassAttribute(editor, list, config, null, null);

			var styleMap = parseStyleMap(list.getAttribute('style'));
			styleMap['--rsmspark-rt-list-marker-color'] = hex;
			editor.dom.setAttrib(list, 'style', buildStyleString(styleMap));
			rebuildStyledList(editor, list, config, null, null);
		});
		syncLock = false;
		editor.nodeChanged();
		syncEditorTextarea(editor);
	}

	function setListClasses(editor, styleClass, markerClass) {
		var config = configFor(editor);

		syncLock = true;
		editor.undoManager.transact(function () {
			var list = ensureListFromSelection(editor);
			if (!list) {
				return;
			}

			if (markerClass !== null) {
				listMarkerClassNames(config).forEach(function (item) {
					editor.dom.removeClass(list, item);
				});
				clearListMarkerInlineStyle(editor, list);
			}

			rebuildStyledList(editor, list, config, styleClass, markerClass);
		});
		syncLock = false;
		editor.nodeChanged();
		syncEditorTextarea(editor);
	}

	function activeListMarkerLabel(editor, config) {
		var hex = activeListMarkerHex(editor);
		if (hex) {
			return labelForHex(config.colors, hex);
		}

		var activeClass = activeListMarkerClass(editor, config);
		if (activeClass && config.markerColorMap && config.markerColorMap[activeClass]) {
			return labelForHex(config.colors, config.markerColorMap[activeClass]);
		}

		return '';
	}

	function registerListEditorHooks(editor, config) {
		// Do not register list formatters here — editor.formatter is unavailable during
		// the plugin factory on WordPress TinyMCE 4 and throws on .register().

		editor.on('ExecCommand', function (event) {
			if (syncLock) {
				return;
			}

			if (event.command !== 'InsertUnorderedList' && event.command !== 'InsertOrderedList') {
				return;
			}

			window.setTimeout(function () {
				var list = findList(editor);
				if (!list || editor.dom.hasClass(list, 'rsmspark-rt-list')) {
					return;
				}

				syncLock = true;
				rebuildStyledList(editor, list, config, 'rsmspark-rt-list--bullet', null);
				syncLock = false;
				editor.nodeChanged();
				syncEditorTextarea(editor);
			}, 0);
		});
	}
	function pluginIsRegistered(tm) {
		return !!(tm && tm.PluginManager && tm.PluginManager.get && tm.PluginManager.get('rsm_rich_text'));
	}

	function registerRsmRichTextPlugin() {
		if (typeof tinymce === 'undefined' || !tinymce.PluginManager) {
			return false;
		}

		if (pluginIsRegistered(tinymce)) {
			return true;
		}

		tinymce.PluginManager.add('rsm_rich_text', initRsmRichTextEditor);
		return true;
	}

	function initRsmRichTextEditor(editor) {
		editor.on('PreInit', function () {
			if (editor.schema && typeof editor.schema.addValidElements === 'function') {
				editor.schema.addValidElements('ul[class|style],ol[class|style],li[class]');
			}
		});

		var config = configFor(editor);
		var i18n = config.i18n || {};
		var sizeClasses = classNamesFromConfig(config.sizes);
		var colorClasses = classNamesFromConfig(config.colors);
		var limits = sizeLimits(config);
		var editorUi = editorUiConfig();

			if (editorUi.mediaRow) {
				registerMediaButton(editor, config);
			}

			registerTokenMenu(
				editor,
				'rsm_rt_size',
				i18n.size || 'Text size',
				config.sizes,
				sizeClasses,
				function (className) {
					applyTokenClass(editor, className, sizeClasses, 'font-size');
				},
				function (items) {
					var menu = buildSizeMenuItems(items, function (item) {
						if (item.px) {
							applyCustomFontSize(editor, item.px, sizeClasses, limits);
						} else if (item.class) {
							applyTokenClass(editor, item.class, sizeClasses, 'font-size');
						}
					});

					menu.push({ text: '-' });
					menu.push({
						text: i18n.customSize || 'Custom size…',
						onclick: function () {
							openCustomSizeDialog(editor, config, sizeClasses);
						},
					});

					return menu;
				},
				function () {
					var activeClass = activeClassOnNode(editor, editor.selection.getNode(), sizeClasses);
					if (activeClass) {
						return labelForClass(config.sizes, activeClass);
					}

					var px = activeInlineFontSizePx(editor);
					if (px) {
						return px + 'px';
					}

					return '';
				}
			);

			registerProfileColorMenu(
				editor,
				'rsm_rt_color',
				i18n.color || 'Text color',
				config,
				colorClasses,
				function (item) {
					if (item.hex) {
						applyCustomColor(editor, item.hex, colorClasses);
					} else if (item.class) {
						applyTokenClass(editor, item.class, colorClasses, 'color');
					}
				},
				function () {
					var activeClass = activeClassOnNode(editor, editor.selection.getNode(), colorClasses);
					if (activeClass) {
						return labelForClass(config.colors, activeClass);
					}

					var hex = activeInlineColorHex(editor);
					if (hex) {
						return hex;
					}

					return '';
				},
				function () {
					return activeInlineColorHex(editor);
				}
			);

			registerTokenMenu(
				editor,
				'rsm_rt_highlight',
				i18n.highlight || 'Highlight',
				config.colors,
				colorClasses,
				function () {},
				function (items) {
					var menu = buildColorMenuItems(items, function (item) {
						if (item.hex) {
							applyCustomHighlight(editor, item.hex, colorClasses);
						}
					});

					menu.push({ text: '-' });
					menu.push({
						text: i18n.customHighlight || 'Custom highlight…',
						onclick: function () {
							openCustomHighlightDialog(editor, config, colorClasses);
						},
					});

					return menu;
				},
				function () {
					var hex = activeInlineBackgroundColorHex(editor);
					return hex || '';
				}
			);

			if (config.profile === 'headline') {
				var effectClasses = config.effectClasses || [];
				var gradientItems = effectClasses
					.filter(function (className) {
						return className.indexOf('gradient') !== -1;
					})
					.map(function (className) {
						return {
							class: className,
							label: i18n.gradient || 'Brand gradient',
						};
					});

				if (gradientItems.length) {
					registerTokenMenu(
						editor,
						'rsm_rt_gradient',
						i18n.gradient || 'Gradient',
						gradientItems,
						effectClasses,
						function (className) {
							applyTokenClass(editor, className, effectClasses);
						}
					);
				}
			}

			registerTokenMenu(
				editor,
				'rsm_rt_font',
				i18n.font || 'Font',
				config.fonts || [],
				colorClasses,
				function () {},
				function (items) {
					var menu = (items || []).map(function (item) {
						return {
							text: item.label,
							onclick: function () {
								if (item.family) {
									applyCustomFontFamily(editor, item.family, colorClasses);
								}
							},
						};
					});

					menu.push({ text: '-' });
					menu.push({
						text: i18n.customFont || 'Custom font stack…',
						onclick: function () {
							openCustomFontDialog(editor, config, colorClasses);
						},
					});

					return menu;
				},
				function () {
					var family = activeInlineFontFamily(editor);
					if (!family) {
						return '';
					}

					var match = (config.fonts || []).filter(function (item) {
						return item.family === family;
					});

					return match.length ? match[0].label : family.split(',')[0];
				}
			);

			if (config.profile === 'body') {
				registerTokenMenu(
					editor,
					'rsm_rt_list',
					i18n.list || 'List style',
					config.listStyles,
					[],
					function (className) {
						setListClasses(editor, className, null);
					}
				);

				registerProfileColorMenu(
					editor,
					'rsm_rt_list_color',
					i18n.listColor || 'List marker color',
					config,
					colorClasses,
					function (item) {
						applyListMarkerColorItem(editor, item);
					},
					function () {
						return activeListMarkerLabel(editor, config);
					},
					function () {
						return activeListMarkerHex(editor);
					}
				);

				safeAddButton(editor, 'rsm_rt_section', {
					text: toolbarTextFor('rsm_rt_section', i18n.sectionTitle || 'Section'),
					tooltip: i18n.sectionTitle || 'Section title',
					onclick: function () {
						var selected =
							editor.selection.getContent({ format: 'text' }) || 'Section title';
						editor.insertContent(
							'<h3 class="rsmspark-rt-section-title">' +
								editor.dom.encode(selected) +
								'</h3>'
						);
					},
				});

				registerListEditorHooks(editor, config);
			}
		}

	if (!registerRsmRichTextPlugin()) {
		var registerAttempts = 0;
		var registerTimer = (window.setInterval || setInterval)(function () {
			registerAttempts++;
			if (registerRsmRichTextPlugin() || registerAttempts > 200) {
				(window.clearInterval || clearInterval)(registerTimer);
			}
		}, 25);
	}
})();
