/* global tinymce, tinyMCE, tinyMCEPreInit, wpforms_settings, wpforms */

// noinspection ES6ConvertVarToLetConst
/**
 * Rich Text field.
 *
 * @since 1.7.0
 */
var WPFormsRichTextField = window.WPFormsRichTextField || ( function( document, window, $ ) { // eslint-disable-line no-var
	/**
	 * Read-only field support object.
	 *
	 * Compatibility logic for the snippet from https://wpforms.com/developers/disable-a-form-field-to-prevent-user-input/.
	 *
	 * @since 1.9.8
	 */
	const readOnly = {

		/**
		 * Disable TinyMCE editor for read-only fields.
		 *
		 * @since 1.9.8
		 *
		 * @param {Object} editor TinyMCE editor instance.
		 */
		disableEditor: ( editor ) => {
			setTimeout( function() {
				try {
					editor.setMode( 'readonly' );
					editor.getBody().setAttribute( 'contenteditable', 'false' );
				} catch ( e ) {}

				const container = editor.getContainer();

				if ( container ) {
					container.style.pointerEvents = 'none';
					container.style.opacity = '0.35';
					container.classList.add( 'wpf-disabled' );
				}
			}, 200 );
		},

		/**
		 * Enable TinyMCE editor for read-only fields.
		 *
		 * @since 1.9.8
		 *
		 * @param {Object} editor TinyMCE editor instance.
		 */
		enableEditor: ( editor ) => {
			setTimeout( function() {
				try {
					editor.setMode( 'design' );
					editor.getBody().setAttribute( 'contenteditable', 'true' );
				} catch ( e ) {}

				const container = editor.getContainer();
				if ( container ) {
					container.style.pointerEvents = 'auto';
					container.style.opacity = '1';
					container.classList.remove( 'wpf-disabled' );
				}
			}, 200 );
		},

		/**
		 * Check if the field is disabled and disable the editor if needed.
		 *
		 * @since 1.9.8
		 *
		 * @param {Object} editor TinyMCE editor instance.
		 */
		checkAndDisableEditor: ( editor ) => {
			const $field = $( editor.getElement() ).closest( '.wpforms-field' );

			if ( $field.hasClass( 'wpforms-field-readonly' ) || $field.hasClass( 'wpf-disable-field' ) ) {
				readOnly.disableEditor( editor );
			}
		},

		/**
		 * Disable textarea fields before TinyMCE initialization.
		 *
		 * @since 1.9.8
		 */
		disableTextAreas: function() { // eslint-disable-line object-shorthand
			$( '.wpforms-field-richtext' ).each( function() {
				const $field = $( this );

				if ( $field.hasClass( 'wpforms-field-readonly' ) || $field.hasClass( 'wpf-disable-field' ) ) {
					const $textarea = $field.find( '.wp-editor-area' );
					if ( $textarea.length ) {
						$textarea.prop( 'readonly', true );
						$field.find( '.wp-editor-wrap' ).addClass( 'wpf-disabled' );
					}
				}
			} );
		},

		/**
		 * Disable the textarea for read-only fields.
		 *
		 * @since 1.9.8
		 *
		 * @param {jQuery} $field jQuery object for .wpforms-field.
		 * @param {jQuery} $wrap  jQuery object for .wp-editor-wrap.
		 *
		 * @return {boolean} True if field is disabled, false otherwise.
		 */
		disableOnHtmlSwitch: ( $field, $wrap ) => {
			if ( $field.hasClass( 'wpforms-field-readonly' ) || $field.hasClass( 'wpf-disable-field' ) ) {
				// Disable the textarea for read-only fields.
				const $textarea = $wrap.find( '.wp-editor-area' );
				$textarea.prop( 'readonly', true );
				$textarea.css( 'opacity', '0.6' );
				$textarea.css( 'pointer-events', 'none' );
				$wrap.addClass( 'wpf-disabled' );

				return true;
			}

			return false;
		},
	};

	/**
	 * Private functions and properties.
	 *
	 * @since 1.7.0
	 *
	 * @type {Object}
	 */
	const vars = {
		mediaPostIdUpdateEvent: false,
	};

	/**
	 * Public functions and properties.
	 *
	 * @since 1.7.0
	 *
	 * @type {Object}
	 */
	const app = {

		/**
		 * Start the engine.
		 *
		 * @since 1.7.0
		 */
		init() {
			$( document )
				.on( 'wpformsReady', app.customizeRichTextField )
				.on( 'wpformsAjaxSubmitSuccessConfirmation', app.updateIframes );

			// Re-initialize tinyMCE in Elementor's popups.
			window.addEventListener( 'elementor/popup/show', function( event ) {
				app.reInitRichTextFields( event.detail.instance.$element );
			} );
		},

		/**
		 * Customize the Rich Text field.
		 *
		 * @since 1.7.0
		 */
		customizeRichTextField() { // eslint-disable-line max-lines-per-function
			const $document = $( document );

			// Check and disable fields before TinyMCE initialization.
			readOnly.disableTextAreas();

			$document.on( 'tinymce-editor-setup', function( event, editor ) {
				// Validate hidden editor textarea on keyup.
				editor.on( 'keyup', function() {
					app.validateRichTextField( editor );
				} );

				editor.on( 'focus', function( e ) {
					$( e.target.editorContainer ).closest( '.wp-editor-wrap' ).addClass( 'wpforms-focused' );
				} );

				editor.on( 'blur', function( e ) {
					$( e.target.editorContainer ).closest( '.wp-editor-wrap' ).removeClass( 'wpforms-focused' );
				} );
			} );

			// Validate on mutation (insert image or any other changes).
			$document.on( 'wpformsRichTextContentChange', function( event, mutation, editor ) {
				app.validateRichTextField( editor );
				app.enableAddMediaButtons( mutation );
			} );

			// Init each field.
			$document.on( 'tinymce-editor-init', function( event, editor ) {
				const docStyle = editor.getDoc().body.style;
				const $body = $( 'body' );

				// Inherit body text font family.
				docStyle.fontFamily = $body.css( 'font-family' );
				docStyle.background = 'transparent';

				// Check if the field is disabled and disable the editor if needed.
				readOnly.checkAndDisableEditor( editor );

				app.initEditorModernMarkupMode( editor );
				app.mediaPostIdUpdate();
				app.observeEditorChanges( editor );
				app.cleanImages( editor );

				$document.trigger( 'wpformsRichTextEditorInit', [ editor ] );
			} );

			// Set `required` property for each of the hidden editor textarea.
			$( 'textarea.wp-editor-area' ).each( function() {
				const $this = $( this );

				if ( $this.hasClass( 'wpforms-field-required' ) ) {
					$this.prop( 'required', true );
				}
			} );

			// Closing media modal on click.
			$document.on( 'click', '.media-modal-close, .media-modal-backdrop', app.enableAddMediaButtons );

			// Closing media modal via ESC key.
			if ( typeof wp !== 'undefined' && typeof wp.media === 'function' ) {
				wp.media.view.Modal.prototype.on( 'escape', function() {
					app.enableAddMediaButtons( 'escapeEvent' );
				} );
			}

			$document.on( 'click', '.switch-html', function() {
				const $wrap = $( this ).closest( '.wp-editor-wrap' );
				const $field = $wrap.closest( '.wpforms-field' );

				setTimeout( function() {
					if ( ! readOnly.disableOnHtmlSwitch( $field, $wrap ) ) {
						$wrap.find( '.wp-editor-area' ).trigger( 'focus' );
					}

					$wrap.addClass( 'wpforms-focused' );
				}, 0 );
			} );

			$document.on( 'click', '.switch-tmce', function( e ) {
				// Prevent the default action of the click event
				e.preventDefault();

				const $wrap = $( this ).closest( '.wp-editor-wrap' ),
					textareaId = $wrap.find( '.wp-editor-area' ).attr( 'id' );

				const editor = tinyMCE.get( textareaId );

				if ( editor ) {
					$wrap.addClass( 'wpforms-focused' );

					// Check if the field is disabled and disable the editor if needed.
					const $field = $wrap.closest( '.wpforms-field' );

					if ( $field.hasClass( 'wpf-disable-field' ) ) {
						readOnly.disableEditor( editor );
					} else {
						// Focus on the editor without causing the jump effect.
						setTimeout( () => {
							editor.focus( false );
						}, 0 );
					}
				}
			} );

			$document.on( 'focus', '.wp-editor-area', function() {
				$( this ).closest( '.wp-editor-wrap' ).addClass( 'wpforms-focused' );
			} );

			$document.on( 'blur', '.wp-editor-area', function() {
				$( this ).closest( '.wp-editor-wrap' ).removeClass( 'wpforms-focused' );
			} );
		},

		/**
		 * Replace special characters in image attributes.
		 *
		 * @since 1.8.3
		 * @param {Object} editor TinyMCE editor instance.
		 */
		cleanImages( editor ) {
			// Get TinyMCE content in raw format.
			const content = editor.getContent( { format: 'raw' } );

			// Create a temporary element to manipulate the content.
			const imageDiv = document.createElement( 'div' );

			// Set the content to the temporary element.
			imageDiv.innerHTML = content;

			// Find all the images in the content.
			const images = imageDiv.querySelectorAll( 'img' );

			// Loop through all the images.
			for ( let i = 0; i < images.length; i++ ) {
				// Replace wrong quote characters.
				images[ i ].outerHTML = images[ i ].outerHTML.replace( /"”|”"|"″|″"/g, '"' );
			}

			// Send a clean image back to TinyMCE.
			editor.setContent( imageDiv.innerHTML );
		},

		/**
		 * Add a media button for WordPress 4.9.
		 *
		 * @since 1.7.0
		 * @deprecated 1.8.7
		 *
		 * @param {Object} editor TinyMCE editor instance.
		 */
		addMediaButton( editor ) {
			// eslint-disable-next-line no-console
			console.warn( 'WARNING! Function "WPFormsRichTextField.addMediaButton()" has been deprecated!' );

			if ( wpforms_settings.richtext_add_media_button ) {
				editor.addButton( 'wp_add_media', {
					tooltip: 'Add Media',
					icon: 'dashicon dashicons-admin-media',
					cmd: 'WP_Medialib',
				} );
			}
		},

		/**
		 * Enable Add Media buttons.
		 *
		 * @since 1.7.0
		 *
		 * @param {object|string} mutation Mutation observer's record or event object.
		 */
		enableAddMediaButtons( mutation ) {
			const isEscapeEvent = mutation === 'escapeEvent';

			if ( ! isEscapeEvent && ! app.isCloseEvent( mutation ) && ! app.isMutationImage( mutation ) ) {
				return;
			}

			$( '.mce-btn-group button i.dashicons-admin-media' ).closest( '.mce-btn' ).removeClass( 'mce-btn-disabled' );
		},

		/**
		 * Is it the close media library event?
		 *
		 * @since 1.7.0
		 *
		 * @param {Object} mutation Mutation observer's record or event object.
		 *
		 * @return {boolean} True if is the close event.
		 */
		isCloseEvent( mutation ) {
			return typeof mutation.target !== 'undefined' && (
				mutation.target.classList.contains( 'media-modal-icon' ) ||
				mutation.target.classList.contains( 'media-modal-backdrop' )
			);
		},

		/**
		 * Is it not a mutation event?
		 *
		 * @since 1.7.0
		 *
		 * @param {Object} mutation Mutation observer's record or event object.
		 *
		 * @return {boolean} True if isn't mutation event.
		 */
		isMutationImage( mutation ) {
			if ( typeof mutation.addedNodes === 'undefined' || typeof mutation.addedNodes[ 0 ] === 'undefined' ) {
				return false;
			}

			let isMutationImage = false;

			mutation.addedNodes.forEach( function( node ) {
				if ( node.tagName === 'IMG' ) {
					isMutationImage = true;

					return false;
				}

				if ( node.tagName === 'A' && node.querySelector( 'img' ) ) {
					isMutationImage = true;

					return false;
				}
			} );

			return isMutationImage;
		},

		/**
		 * Disable Add Media buttons.
		 *
		 * @since 1.7.0
		 */
		disableAddMediaButtons() {
			$( '.mce-btn-group button i.dashicons-admin-media' ).closest( '.mce-btn' ).addClass( 'mce-btn-disabled' );
		},

		/**
		 * Update Fake Post ID according to the Field ID.
		 *
		 * @since 1.7.0
		 */
		mediaPostIdUpdate() {
			if ( vars.mediaPostIdUpdateEvent ) {
				return;
			}

			$( '.wpforms-field-richtext-media-enabled .mce-toolbar .mce-btn' ).on( 'click touchstart', function( e ) {
				const $this = $( e.target );

				if ( ! $this.hasClass( 'dashicons-admin-media' ) && $this.find( '.dashicons-admin-media' ).length === 0 ) {
					return;
				}

				const formId = $this.closest( 'form' ).data( 'formid' ),
					fieldId = $this.closest( '.wpforms-field-richtext' ).data( 'field-id' );

				// Replace the digital parts with the current form and field IDs.
				wp.media.model.settings.post.id = 'wpforms-' + formId + '-field_' + fieldId;

				app.disableAddMediaButtons();
			} );

			vars.mediaPostIdUpdateEvent = true;
		},

		/**
		 * Observe changes inside the editor's iframe.
		 *
		 * @since 1.7.0
		 *
		 * @param {Object} editor TinyMCE editor instance.
		 */
		observeEditorChanges( editor ) {
			// Observe changes inside editor's iframe.
			const observer = new MutationObserver( function( mutationsList ) {
				for ( const key in mutationsList ) {
					if ( mutationsList[ key ].type === 'childList' ) {
						$( document ).trigger( 'wpformsRichTextContentChange', [ mutationsList[ key ], editor ] );
					}
				}
			} );

			observer.observe(
				editor.iframeElement.contentWindow.document.body,
				{
					childList: true,
					subtree: true,
					attributes: true,
				}
			);
		},

		/**
		 * Validate Rich Text field.
		 *
		 * @since 1.7.0
		 *
		 * @param {Object} editor TinyMCE editor instance.
		 */
		validateRichTextField( editor ) {
			if ( ! editor || ! $( editor.iframeElement ).closest( 'form' ).data( 'validator' ) ) {
				return;
			}

			const $textarea = $( '#' + editor.id );

			// We should save and validate if only the editor's content has the real changes.
			if ( editor.getContent() === $textarea.val() ) {
				return;
			}

			editor.save();

			$textarea.valid();
		},

		/**
		 * Re-initialize tinyMCEs in a given form (container).
		 *
		 * @since 1.7.0
		 *
		 * @param {jQuery} $form Form container.
		 */
		reInitRichTextFields( $form ) {
			if ( typeof tinyMCEPreInit === 'undefined' || typeof tinymce === 'undefined' ) {
				return;
			}

			$form.find( '.wp-editor-area' ).each( function() {
				const id = $( this ).attr( 'id' );

				if ( tinymce.get( id ) ) {
					// Remove the existing editor.
					tinyMCE.execCommand( 'mceRemoveEditor', false, id );
				}

				window.quicktags( tinyMCEPreInit.qtInit[ id ] );
				$( '#' + id ).css( 'visibility', 'initial' );

				tinymce.init( tinyMCEPreInit.mceInit[ id ] );
			} );
		},

		/**
		 * Initialize tinyMCE in Modern Markup mode.
		 *
		 * @since 1.8.1
		 *
		 * @param {Object} editor TinyMCE editor instance.
		 */
		initEditorModernMarkupMode( editor ) {
			if ( ! wpforms.isModernMarkupEnabled() || window.WPFormsEditEntry || ! window.WPForms.FrontendModern ) {
				return;
			}

			const docStyle = editor.getDoc().body.style;
			const $el = $( editor.getElement() );
			const $field = $el.closest( '.wpforms-field' );
			const $form = $el.closest( '.wpforms-form' );
			const cssVars = window.WPForms.FrontendModern.getCssVars( $form );
			const inputHeight = cssVars[ 'field-size-input-height' ] ? cssVars[ 'field-size-input-height' ].replace( 'px', '' ) : 43;
			const sizeK = {
				small: 1.80,
				medium: 2.79,
				large: 5.12,
			};

			let fieldSize = 'medium';
			fieldSize = $field.hasClass( 'wpforms-field-small' ) ? 'small' : fieldSize;
			fieldSize = $field.hasClass( 'wpforms-field-large' ) ? 'large' : fieldSize;

			const width = editor.getWin().clientWidth;
			const height = inputHeight * sizeK[ fieldSize ];
			editor.theme.resizeTo( width, height );

			docStyle.color = cssVars[ 'field-text-color' ];
			docStyle.fontSize = cssVars[ 'field-size-font-size' ];
		},

		/**
		 * Create an iframe from a div inside the confirmation message.
		 *
		 * @since 1.9.2
		 *
		 * @param {Event} event Event instance from the `wpformsAjaxSubmitSuccessConfirmation` hook.
		 */
		updateIframes( event ) {
			$( event.target ).find( '.wpforms-iframe' ).each( window?.WPFormsIframe?.update );
		},

		/**
		 * Set the field as read-only.
		 *
		 * @since 1.9.8
		 *
		 * @param {jQuery} $field Field container object.
		 */
		lockField( $field ) {
			const editor = tinyMCE.get( $field.find( '.wp-editor-area' ).attr( 'id' ) );
			readOnly.disableEditor( editor );
		},

		/**
		 * Remove the read-only state from the field.
		 *
		 * @since 1.9.8
		 *
		 * @param {jQuery} $field Field object.
		 */
		unlockField( $field ) {
			const editor = tinyMCE.get( $field.find( '.wp-editor-area' ).attr( 'id' ) );
			readOnly.enableEditor( editor );
		},
	};

	// Provide access to public functions/properties.
	return app;
}( document, window, jQuery ) );

WPFormsRichTextField.init();
