(function( $ ) {		
	'use strict';

	const template = document.createElement( 'template' );

	template.innerHTML = `
		<style>
			:host {                             
                display: block;  
                width: 100%;      
            }

			:host([is-video]) {
				max-width: var( --plyr-video-width, 100% );
			}

			:host([is-video]) #root {
				position: relative;
				padding-top: var( --plyr-video-ratio, 56.25% );
				width: 100%;
				height: 0;
			}

			:host([is-video]) #root .plyr {
				position: absolute;
				inset: 0;
				border-radius: 3px;
				width: 100%;
				height: 100%;
			}

			:host([is-video]) #root .plyr__control--overlaid {
				--plyr-control-spacing: 15px;
			}

			:host([is-video]) #root .plyr__control--overlaid svg {
				--plyr-control-icon-size: 27px;
			}
		</style>
		<div id="root"></div>
	`;

	/**
	 * Load script files.
	 */
	const scriptPromises = {};

	var loadScript = ( file ) => {
		if ( scriptPromises[ file.id ] ) {
			return scriptPromises[ file.id ];
		}

		scriptPromises[ file.id ] = new Promise(( resolve, reject ) => {
			const existing = document.getElementById( file.id );
			if ( existing ) {
				resolve();
				return;
			}

			const script = document.createElement( 'script' );
			script.id    = file.id;
			script.src   = file.src;

			script.onload  = () => resolve();
			script.onerror = () => reject();

			document.body.appendChild( script );
		});

		return scriptPromises[ file.id ];
	};

	/**
	 * Define a custom HTML element class for <aiopod-media-preview>
	 */
	class AIOPODMediaPreview extends HTMLElement {

		/**
		 * Element created.
		 */
		constructor() {
			super();

			// Attach Shadow DOM to the component
			const shadowDom = this.attachShadow({ mode: 'open' });
			this.shadowRoot.appendChild( template.content.cloneNode( true ) ); 

			// Set references to the DOM elements from the component's template
            this.rootEl = shadowDom.querySelector( '#root' );

			// Set references to the public properties used by the component
			this.player = null;
		}

		/**
		 * Called when the element is added to the document.
		 */
		connectedCallback() {
			if ( this.player ) return; // Prevent multiple initializations

			// Lazy-load dependencies and initialize when ready
			const plyrPromise =	this._loadStyle( aiopod_admin.plugin_url + 'vendor/plyr/plyr.css?ver=3.7.8' )
				.then( () => {
					return window.Plyr ? Promise.resolve() : loadScript({
						id: 'aio-podcast-studio-plyr-js',
						src: aiopod_admin.plugin_url + 'vendor/plyr/plyr.polyfilled.js?ver=3.7.8'
					});
				});

			plyrPromise.then( () => {
				this._initPlayer();
			}).catch( error => {
				console.error( 'Failed to initialize Plyr:', error );
			});
		}

		/**
		 * Called when the element is removed from the document.
		 */
		disconnectedCallback() {
			// Destroy HLS | Dash
			this._destroyStreamingProviders();

			// Destroy Plyr
			if ( this.player ) {
				this.player.destroy();
				this.player = null;
			}
		}

		/**
         * Define getters and setters for attributes.
         */
		get src() {
			return this.getAttribute( 'src' ) || '';
		}

		/**
         * Define private methods.
         */
		_initPlayer() {
			const src = this.src || '';

			if ( ! src ) {
				console.error( 'Media source not found.' );
				return;
			}

			const mediaType = this.hasAttribute( 'is-video' ) ? 'video' : 'audio';

			let media;

			if ( /youtube\.com|youtu\.be/.test( src ) ) { // YouTube
				media = document.createElement( 'div' );
				media.setAttribute( 'data-plyr-provider', 'youtube' );
				media.setAttribute( 'data-plyr-embed-id', src );
			} else if ( /vimeo\.com/.test( src ) ) { // Vimeo
				media = document.createElement( 'div' );
				media.setAttribute( 'data-plyr-provider', 'vimeo' );
				media.setAttribute( 'data-plyr-embed-id', src );
			} else {
				media = document.createElement( mediaType );
				media.setAttribute( 'controls', '' );
				media.setAttribute( 'preload', 'metadata' );
				media.setAttribute( 'playsinline', '' );
				media.src = src;	
			}

			this.rootEl.appendChild( media );

			const options = {};
			options.iconUrl = aiopod_admin.plugin_url + 'vendor/plyr/plyr.svg';

			const player = new Plyr( media, options );
			this.player  = player;

			// HLS
			if ( /\.m3u8(\?|$)/i.test( src ) ) {
				const isSafari = /^((?!chrome|android).)*safari/i.test( navigator.userAgent );
				if ( ! isSafari ) {
					this._initHls( media, src );
				}
			}

			// Dash
			if ( /\.mpd(\?|$)/i.test( src ) ) {
				this._initDash( media, src );
			}	

			// OnReady
			player.on( 'ready', () => {
				// YouTube 
				if ( player.provider === 'youtube' && player.embed && player.embed.getDuration ) {
					const duration = player.embed.getDuration();
					if ( ! isNaN( duration) && duration > 0 ) {
						this._triggerEvent( 'durationchange', { player: player, duration: duration } );
					}
	
					return;
				}

				// Vimeo
				if ( player.provider === 'vimeo' && player.embed && player.embed.getDuration ) {
					player.embed.getDuration().then(duration => {
						if ( ! isNaN( duration) && duration > 0 ) {
							this._triggerEvent( 'durationchange', { player: player, duration: duration } );
						}
					});

					return;
				}
			});

			// HTML5 / HLS / Dash
			player.once( 'loadedmetadata', () => {
				const duration = player.duration || 0;
				if ( ! isNaN( duration) && duration > 0 ) {
					this._triggerEvent( 'durationchange', { player: player, duration: duration } );
				}
			});
		}

		_initHls( media, src ) {
			const hlsPromise = window.Hls ? Promise.resolve() : loadScript({
				id: 'aio-podcast-studio-hls-js',
				src: aiopod_admin.plugin_url + 'vendor/plyr/hls.min.js?v=1.5.17'
			});

			hlsPromise.then( () => {
				if ( ! this.player ) return;
				const player = this.player;

				this._hls = new Hls();
				this._hls.loadSource( src );
				this._hls.attachMedia( media );

				this._onLanguageChange = () => {
					setTimeout( () => {
						if ( this._hls ) {
							this._hls.subtitleTrack = player.currentTrack;
						}
					}, 50 );
				};

				player.on( 'languagechange', this._onLanguageChange );
			}).catch( error => {
				console.error( 'Failed to initialize HLS:', error );
			});
		}

		_initDash( media, src ) {
			const dashPromise = window.dashjs ? Promise.resolve() : loadScript({
				id: 'aio-podcast-studio-dash-js',
				src: aiopod_admin.plugin_url + 'vendor/plyr/dash.all.min.js?v=4.7.4'
			});

			dashPromise.then( () => {
				if ( ! this.player ) return;

				this._dash = dashjs.MediaPlayer().create();
  				this._dash.initialize( media, src, false );
			}).catch( error => {
				console.error( 'Failed to initialize Dash:', error );
			});
		}

		_destroyStreamingProviders() {
			// Remove Plyr events
			if ( this.player && this._onLanguageChange ) {
				this.player.off( 'languagechange', this._onLanguageChange );
				this._onLanguageChange = null;
			}

			// Destroy HLS
			if ( this._hls ) {
				this._hls.destroy();
				this._hls = null;
			}

			// Destroy DASH
			if ( this._dash ) {
				this._dash.reset();
				this._dash = null;
			}
		}

		_triggerEvent( event, data ) {
			const customEvent = new CustomEvent( event, {					
				bubbles: true,
				composed: true,
				detail: data
			});

			this.dispatchEvent( customEvent );
		}
		
		/**
         * Define private async methods.
         */		
		async _loadStyle( url ) {
			if ( ! AIOPODMediaPreview.plyrCssText ) {
				const res = await fetch( url );
				if ( ! res.ok ) throw new Error( res.status );
				
				AIOPODMediaPreview.plyrCssText = await res.text();
			}

			if ( 'adoptedStyleSheets' in Document.prototype && 'replaceSync' in CSSStyleSheet.prototype ) {
				const sheet = new CSSStyleSheet();
				sheet.replaceSync( AIOPODMediaPreview.plyrCssText );
				this.shadowRoot.adoptedStyleSheets = [ sheet ];
			} else {
				const style = document.createElement( 'style' );
				style.textContent = AIOPODMediaPreview.plyrCssText;
				this.shadowRoot.appendChild( style );
			}
		}		

	}

	AIOPODMediaPreview.plyrCssText = null;	

	/**
 	 * Init metabox UI (Tabs + Accordion). 
 	 */
	function initMetaboxUI( container ) {
		const $container = $( container );

		// Tabs
		$container.find( '.aiopod-tab' ).on( 'click', function( e ) {
			e.preventDefault();

			const $this  = $( this );
			const target = $this.data( 'target' );

			$container.find( '.aiopod-tab' ).removeClass( 'aiopod-active' );
			$this.addClass( 'aiopod-active' );

			$container.find( '.aiopod-tab-content' ).hide();
			$container.find( target ).fadeIn( 200 );
		});

		// Accordion
		$container.find( '.aiopod-accordion-header' ).on( 'click', function( e ) {
			e.preventDefault();

			const $header    = $( this );
			const $accordion = $header.closest( '.aiopod-accordion' );

			if ( $accordion.data( 'collapsible' ) ) {
				const $icon = $header.find( '.dashicons' );

				$accordion.find( '.aiopod-accordion-body' ).slideToggle( 200 );
				$header.toggleClass( 'aiopod-open' );
				$icon.toggleClass( 'dashicons-arrow-down-alt2 dashicons-arrow-up-alt2' );
			}
		});
	}
	
	/**
	 * Render media uploader.
	 */
	function renderMediaUploader( callback ) { 
		var fileFrame;

		// If an instance of fileFrame already exists, then we can open it rather than creating a new instance.
		if ( fileFrame ) {
			fileFrame.open();
			return false;
		}

		// Use the wp.media library to define the settings of the media uploader.
		fileFrame = wp.media.frames.file_frame = wp.media({
			frame: 'post',
			state: 'insert',
			multiple: false
		});		

		// Setup an event handler for what to do when a media has been selected.
		fileFrame.on( 'insert', function() { 
			// Read the JSON data returned from the media uploader.
			var json = fileFrame.state().get( 'selection' ).first().toJSON();
		
			// First, make sure that we have the URL of the media to display.
			if ( json.url.trim().length == 0 ) {
				return false;
			}
		
			callback( json );			 
		});

		fileFrame.state( 'embed' ).on( 'select', function() {
			// Read the JSON data returned from the media uploader.
			var json = fileFrame.state().props.toJSON();

			// First, make sure that we have the URL of the media to display.
			if ( json.url.trim().length == 0 ) {
				return false;
			}

			json.id = 0;
			callback( json );			 
		});

		// Now display the actual fileFrame.
		fileFrame.on( 'open', function() { 
			jQuery( '#menu-item-gallery, #menu-item-playlist, #menu-item-video-playlist' ).hide();
		});

		fileFrame.open(); 
	}

	/**
 	 * Validate Rss Feed URL.
 	 */
	function validateRssFeedUrl() {
		const $wrapper = $( '#aiopod-form-control-rss_feed_url' );
		const $input   = $( '#aiopod-field-rss_feed_url' );		
		const $status  = $( '#aiopod-form-control-import_podcast .aiopod-form-status' );

		const url        = $input.val().trim();
		const isValidUrl = /^https?:\/\/[^\s/$.?#].[^\s]*$/i.test( url );

		if ( url === '' ) {
			$wrapper.addClass( 'aiopod-form-invalid' );
			$status.html( '<span class="aiopod-text-error"><span class="dashicons dashicons-dismiss"></span> ' + aiopod_admin.i18n.feed_required + '</span>' );
			return false;
		}

		if ( ! isValidUrl ) {
			$wrapper.addClass( 'aiopod-form-invalid' );
			$status.html( '<span class="aiopod-text-error"><span class="dashicons dashicons-dismiss"></span> ' + aiopod_admin.i18n.feed_invalid + '</span>' );
			return false;
		}

		$wrapper.removeClass( 'aiopod-form-invalid' );
		$status.html( '' );
		return true;
	}

	/**
	 * Converts a time string (H:MM:SS, MM:SS, or SS) to total seconds.
	 */
	function timeStringToSeconds( timeStr ) {
		let parts   = timeStr.split( ':' );
		let seconds = 0;

		if ( parts.length === 3 ) { // H:MM:SS			
			seconds += parseInt( parts[0], 10 ) * 3600;
			seconds += parseInt( parts[1], 10 ) * 60;
			seconds += parseInt( parts[2], 10 );
		} else if ( parts.length === 2 ) { // MM:SS			
			seconds += parseInt( parts[0], 10 ) * 60;
			seconds += parseInt( parts[1], 10 );
		} else if ( parts.length === 1 ) { // SS			
			seconds += parseInt( parts[0], 10 );
		}

		return seconds;
	}

	/**
	 * Removes a dot (.) at the end of a string.
	 */
	function removeEndingDot( str ) {
		return str.charAt( str.length - 1 ) === '.' ? str.slice( 0, -1 ) : str;
	}

	/**
 	 * Copy to Clipboard.
 	 */
	function copyToClipboard( value ) {
		const input = document.createElement( 'input' );
		input.value = value;

		document.body.appendChild( input );		
		input.focus();
		input.select();

		document.execCommand( 'copy' );

		input.remove();
		alert( aiopod_admin.i18n.copied + "\n" + value );
	}

	/**
 	 * Replace element classes with pattern matching.
 	 */
	$.fn.aiopodReplaceClass = function( pattern, additions ) {
		this.removeClass(function( index, classes ) {
			var matches = classes.match( pattern );
			return matches ? matches.join( ' ' ) : '';	
		}).addClass( additions );

		return this;
	};

	/**
	 * Called when the page has loaded.
	 */
	$(function() {
		
		// Register custom elements.
		if ( ! customElements.get( 'aiopod-media-preview' ) ) {
			customElements.define( 'aiopod-media-preview', AIOPODMediaPreview );
		}
		
		// Common: Init metabox UI (Tabs + Accordion).
		$( '.aiopod-metabox-ui' ).each(function() {
			initMetaboxUI( this );
		});

		// Common: Accordion - Allow only one section to be open at a time.
		$( document ).on( 'click', '.aiopod-details details > summary', function( event ) {
			event.preventDefault();

			const $summary = $( this );
			const $details = $summary.parent( 'details' );
			const $content = $details.find( '.aiopod-details-content' );

			const speed = 250;

			// CLOSE (same item)
			if ( $details.prop( 'open' ) ) {
				$content.stop( true, true ).slideUp( speed, function() {
					$details.prop( 'open', false );
				});

				return;
			}

			// CLOSE others in same accordion
			$details.closest( '.aiopod-details' ).find( 'details[open]' ).not( $details ).each(function() {
				const $other = $( this );
				$other.find( '.aiopod-details-content' ).stop( true, true ).slideUp( speed, function() {
					$other.prop( 'open', false );
				});
			});

			// OPEN this
			$content.stop( true, true ).hide().slideDown( speed );
			$details.prop( 'open', true );
		});

		// Common: Upload files.
		$( document ).on( 'click', '.aiopod-upload-media', function( event ) { 
            event.preventDefault();

			const $this = $( this );

			renderMediaUploader( function( json ) {
				$this.closest( '.aiopod-input-group' )
					.find( 'input[type=text]' )
					.val( json.url )
					.trigger( 'change', json );
			} );
		});
		
		// Common: Init color picker.
		if ( $.fn.wpColorPicker ) {
			$( '.aiopod-color-picker' ).wpColorPicker();

			$( document ).on( 'widget-added widget-updated', function( event, widget ) {
				widget.find( '.aiopod-color-picker' ).wpColorPicker({
					change: _.throttle( function() { // For Customizer
						$( this ).trigger( 'change' );
					}, 3000)
				});
			});
		}

		// Common: Copy URL.
		$( '.aiopod-copy-url' ).on( 'click', function() {
			const url = $( this ).data( 'url' );
			copyToClipboard( url );			
		});

		// Common: Copy shortcode.
		$( '.aiopod-copy-shortcode' ).on( 'click', function() {
			const shortcode = $( this ).data( 'shortcode' );
			copyToClipboard( shortcode );
		});

		// Common: Open dialog modal.
		$( '.aiopod-modal-button' ).on( 'click', function() {
			const targetId = $( this ).data( 'target' );
			const dialog   = document.getElementById( targetId );
			
			if ( dialog ) {
				dialog.showModal();
			}
		});

		// Common: Close dialog on backdrop click.
		$( document ).on( 'click', 'dialog.aiopod-modal', function( event ) {
			if ( event.target === this ) {
				this.close();
			}
		});

		// Common: Close dialog via close button.
		$( document ).on( 'click', '.aiopod-modal__close', function() {
			$( this ).closest( 'dialog' ).get( 0 ).close();
		});

		// Common: Repeatable fields.
		$( '.aiopod-repeatable-ui' ).each(function() {
			const $containerEl = $( this );

			// Add new field.
			$containerEl.find( '.aiopod-button-add' ).on( 'click', function( event ) { 
				event.preventDefault();
				
				const href     = $( this ).data( 'href' );
				const template = document.querySelector( href );

				if ( template !== null ) {
					const el = template.content.cloneNode( true );	
							
					$containerEl.find( 'tbody' ).append( el );
					$containerEl.find( 'table' ).show();
				}
			});

			// Add a field by default.
			if ( $containerEl.find( 'tr' ).length === 0 ) {
				$containerEl.find( '.aiopod-button-add' ).trigger( 'click' );
			}	
			
			// Delete a field.
			$containerEl.on( 'click', '.aiopod-button-delete', function( event ) { 
				event.preventDefault();	

				$( this ).closest( 'tr' ).remove(); 

				if ( $containerEl.find( 'tr' ).length === 0 ) {
					$containerEl.find( 'table' ).hide();
				}
			});
			
			// Make fields sortable.
			if ( $.fn.sortable ) {
				const $el = $containerEl.find( 'tbody' );
							
				if ( $el.hasClass( 'ui-sortable' ) ) {
					$el.sortable( 'destroy' );
				}
					
				$el.sortable({
					handle: '.aiopod-sort-handle',
					helper: function(e, ui) {
						// ui is the original element being dragged
						const $originals = ui.children();
						const $helper    = ui.clone(); // Clone the original element

						// Iterate through the children of the helper and set their widths
						$helper.children().each(function( index ) {
							$( this ).width( $originals.eq( index ).width() );
						});

						return $helper;
					}
				}).disableSelection();
			}
		});

		// Podcasts / Episodes: Toggle preview image based on the user input.
		$( '#aiopod-field-image' ).on( 'blur change', function( event, data ) {
			if ( data && data.hasOwnProperty( 'id' ) ) {
				$( '#aiopod-field-image_id' ).val( data.id );
			} else if ( 'change' === event.type ) {
				// Manual URL change: drop the stale attachment ID so it never points to a different image.
				$( '#aiopod-field-image_id' ).val( '' );
			}

			let url = $( this ).val();			
			if ( ! url ) {
				url = $( this ).data( 'default' );
			}

			$( '#aiopod-form-control-image' ).find( 'img' ).attr( 'src', url ); 
		});	

		// Podcasts / Episodes: Bind the default image on error.
		$( '#aiopod-form-control-image img' ).on( 'error', function() {
			const $img        = $( this );			
			const currentSrc  = $img.attr( 'src' );
			const fallbackSrc = $img.data( 'default' );

			// No fallback defined
			if ( ! fallbackSrc ) {
				return;
			}

			// If we're already showing the fallback, do nothing
			if ( currentSrc === fallbackSrc ) {
				return;
			}

			// Fallback
			$img.attr( 'src', fallbackSrc );
		});

		// Podcasts: Fill the RSS Feed URL field with the sample feed URL
		$( '#aiopod-link-sample_feed_url' ).on( 'click', function( event ) {
			event.preventDefault();

			$( '#aiopod-field-rss_feed_url' ).val( $( this ).data( 'feed-url' ) ).trigger( 'focus' );
			validateRssFeedUrl();
		});
		
		// Podcasts: Import from RSS Feed
		let isImporting    = false;
		let currentRequest = null;

		$( '#aiopod-button-import_podcast' ).on( 'click', function( event ) {
			event.preventDefault();

			if ( isImporting ) {
				return;
			}

			if ( ! validateRssFeedUrl() ) {
				return;
			}

			const $this    = $( this );
			const $wrapper = $( '#aiopod-form-control-rss_feed_url' );
			const $input   = $( '#aiopod-field-rss_feed_url' );
			const $status  = $( '#aiopod-form-control-import_podcast .aiopod-form-status' );			

			$status.html( '<span class="spinner"></span> <span class="aiopod-text-success">' + removeEndingDot( aiopod_admin.i18n.import_status ) + '<span class="aiopod-animate-dots"></span></span>' );
			$this.prop( 'disabled', true );

			// Prepare data
			const data = {
				action: 'aiopod_import_podcast',
				security: aiopod_admin.ajax_nonce,
				podcast_id: 0,
				rss_feed_url: $input.val().trim(),
				update_existing_content: 0,
				set_featured_image: 0,
				offset: 0,
				limit: 20,
				total: 0,
				imported: 0,
				updated: 0,
				skipped: 0,
				failed: 0,								
				auto_update_episodes: ( $( '#aiopod-field-auto_update_episodes' ).is( ':checked' ) ? 1 : 0 ),
				auto_update_frequency: $( '#aiopod-field-auto_update_frequency' ).val()
			};	
			
			if ( $( '#post_ID' ).length > 0 ) {
				data.podcast_id = parseInt( $( '#post_ID' ).val() );
			}

			if ( $( '#aiopod-field-update_existing_content' ).length > 0 ) {
				data.update_existing_content = $( '#aiopod-field-update_existing_content' ).is( ':checked' ) ? 1 : 0;
			}

			if ( $( '#aiopod-field-set_featured_image' ).length > 0 ) {
				data.set_featured_image = $( '#aiopod-field-set_featured_image' ).is( ':checked' ) ? 1 : 0;
			}

			// Downloading external featured images is slow, so use a smaller batch
			// to keep each request within the host's max execution time. The
			// server-side time budget in import_episodes() is the real safeguard;
			// this just reduces the upper bound of episodes per request.
			if ( data.set_featured_image ) {
				data.limit = 10;
			}

			// Recursive import function
			const importBatch = function() {
				// Make AJAX request
				currentRequest = $.post( ajaxurl, data, ( response ) => {
					if ( ! response.success ) {
						isImporting    = false;
						currentRequest = null;

						$this.prop( 'disabled', false );
						
						$input.val( '' );
						$wrapper.addClass( 'aiopod-form-invalid' );
						$status.html( '<span class="aiopod-text-error"><span class="dashicons dashicons-dismiss"></span> ' + response.data.error + '</span>' );
						return;
					}

					data.podcast_id = parseInt( response.data.podcast_id );
					data.offset     = parseInt( response.data.offset );
					data.total	    = parseInt( response.data.total );		
					data.imported   = parseInt( response.data.imported );
					data.updated    = parseInt( response.data.updated );
					data.skipped    = parseInt( response.data.skipped );
					data.failed     = parseInt( response.data.failed );					

					// Update status, import next batch or complete
					if ( response.data.step === 'process' ) {	
						$status.html( '<span class="spinner"></span> <span class="aiopod-text-success">' + removeEndingDot( response.data.message ) + '<span class="aiopod-animate-dots"></span></span>' );					
						setTimeout( importBatch, 100 ); // Import next batch
					} else if ( response.data.step === 'complete' ) {
						$status.html( '<span class="aiopod-text-success"><span class="dashicons dashicons-yes-alt"></span> ' + removeEndingDot( response.data.message ) + '<span class="aiopod-animate-dots"></span></span>' );

						isImporting    = false;
						currentRequest = null;

						setTimeout( function() {
							window.location.href = response.data.redirect;
						}, 1000 );
					}
				}, 'json' ).fail( function() {
					isImporting    = false;
					currentRequest = null;

					$this.prop( 'disabled', false );

					$status.html( '<span class="aiopod-text-error"><span class="dashicons dashicons-dismiss"></span> ' + aiopod_admin.i18n.import_failed + '</span>' );
				});
			}

			// Start first batch
			isImporting = true;
			importBatch();
		});
		
		// Podcasts: Validate RSS Feed URL
		let feedValidationTimeout;

		$( '#aiopod-field-rss_feed_url' ).on( 'keyup', function( event ) {
			clearTimeout( feedValidationTimeout );		
			feedValidationTimeout = setTimeout( validateRssFeedUrl, 200 );
		});

		// Podcasts: Prevent the user from accidentally leaving the page while a podcast import is in progress.
		// Displays a browser confirmation dialog and aborts any active AJAX request if the page unloads.
		window.addEventListener( 'beforeunload', function( event ) {
			if ( isImporting ) {
				event.preventDefault();
				event.returnValue = '';
				return '';
			}
		});

		window.addEventListener( 'unload', function() {
			if ( currentRequest ) {
				currentRequest.abort();
			}
		});

		// Podcasts: Sync featured image fields
		$( '.aiopod-sync-featured-image' ).on( 'change', function() {
			const isChecked = $( this ).prop( 'checked' );
			$( '.aiopod-sync-featured-image' ).not( this ).prop( 'checked', isChecked );
		});
		
		// Podcasts: Toggle fields based on the "Auto-update Episodes?" field.
		$( '#aiopod-field-auto_update_episodes' ).on( 'change', function() {			
			if ( this.checked ) {
				$( '#aiopod-form-control-auto_update_frequency' ).show(); 
			} else {
				$( '#aiopod-form-control-auto_update_frequency' ).hide(); 
			}
		});

		// Episodes: Toggle video/audio preview based on the user input.
		if ( $( '#aiopod-field-episode_file' ).length > 0 ) {
			let episodeDetails = [];

			episodeDetails.push({
				url: $( '#aiopod-field-episode_file' ).val(),
				id: $( '#aiopod-field-episode_file_id' ).val(),
				mime: $( '#aiopod-field-episode_file_type' ).val(),
				filesizeInBytes: $( '#aiopod-field-episode_file_size' ).val(),
				fileLength: $( '#aiopod-field-episode_duration' ).val()
			});

			$( '#aiopod-field-episode_file' ).on( 'blur change', function( event, data ) {
				let src  = $( this ).val();
				let html = '';

				if ( data ) {
					if ( ! episodeDetails.find( item => item.url === data.url ) ) {
						episodeDetails.push(data);
					}					
				} else {
					data = {};

					episodeDetails.forEach(item => {
						if ( item.url == src ) {
							data = item;
							return;
						}
					});
				}	
				
				$( '#aiopod-field-episode_file_id' ).val( data.id || '' );
				$( '#aiopod-field-episode_file_type' ).val( data.mime || '' );
				$( '#aiopod-field-episode_file_size' ).val( data.filesizeInBytes || '' );
				$( '#aiopod-field-episode_duration' ).val( data.fileLength ? timeStringToSeconds( data.fileLength ) : '' );

				if ( src ) {	
					const isAudio = /\.(mp3|m4a|aac|ogg|wav|flac)(\?|$)/i.test( src );	

					if ( isAudio ) {
						html += '<aiopod-media-preview src="' + src + '"></aiopod-media-preview>';
					} else {
						html += '<aiopod-media-preview src="' + src + '" is-video></aiopod-media-preview>';
					}
				}
				
				$( '#aiopod-form-control-episode_file .aiopod-media-preview' ).html( html );	
			}).trigger( 'blur' );
		}

		// Episodes: Update media duration dynamically
		$( document ).on( 'durationchange', 'aiopod-media-preview', function( event ) {
			const duration = Math.round( event.detail.duration );
			$( '#aiopod-field-episode_duration' ).val( duration );
		});

		// Episodes: Toggle fields based on the "Enable Transcription?" field.
		$( '#aiopod-field-enable_transcription' ).on( 'change', function() {			
			if ( this.checked ) {
				$( '#aiopod-form-control-transcription' ).show(); 
			} else {
				$( '#aiopod-form-control-transcription' ).hide(); 
			}
		});

		// Episodes: Toggle fields based on the "Enable Chapters?" field.
		$( '#aiopod-field-enable_chapters' ).on( 'change', function() {			
			if ( this.checked ) {
				$( '#aiopod-form-control-chapters' ).show(); 
			} else {
				$( '#aiopod-form-control-chapters' ).hide(); 
			}
		});

		// Categories / Participants: Upload Image.
		$( '#aiopod-terms-upload-image' ).on( 'click', function( event ) { 
            event.preventDefault();

			renderMediaUploader(function( json ) {
				$( '#aiopod-terms-image-wrapper' ).html( '<img src="' + json.url + '" alt="" loading="lazy" decoding="async" />' );

				$( '#aiopod-terms-image' ).val( json.url );
				$( '#aiopod-terms-image_id' ).val( json.id );				
			
				$( '#aiopod-terms-upload-image' ).hide();
				$( '#aiopod-terms-remove-image' ).show();
			}); 
        });
		
		// Categories / Participants: Remove Image.
		$( '#aiopod-terms-remove-image' ).on( 'click', function( event ) {														 
            event.preventDefault();					
			
			$( '#aiopod-terms-image-wrapper' ).html( '' );

			$( '#aiopod-terms-image' ).val( '' );
			$( '#aiopod-terms-image_id' ).val( '' );			
			
			$( '#aiopod-terms-remove-image' ).hide();
			$( '#aiopod-terms-upload-image' ).show();	
		});
		
		// Categories / Participants: Clear the custom fields.
		$( document ).ajaxComplete(function( e, xhr, settings ) {			
			if ( $( '#aiopod-terms-image' ).length && settings.data ) {	
				var queryStringArr = settings.data.split( '&' );
			   
				if ( $.inArray( 'action=add-tag', queryStringArr ) !== -1 ) {
					var response = $( xhr.responseXML ).find( 'term_id' ).text();

					if ( response ) {						
						$( '#aiopod-terms-image-wrapper' ).html( '' );	
						
						$( '#aiopod-terms-image' ).val( '' );	
						$( '#aiopod-terms-image_id' ).val( '' );				
						
						$( '#aiopod-terms-remove-image' ).hide();
						$( '#aiopod-terms-upload-image' ).show();
					}
				}		
			}			
		});

		// Settings: Bind section ID.
		$( '#aiopod-settings .form-table' ).each(function() { 
			var str = $( this ).find( 'tr:first th label' ).attr( 'for' );
			var id  = str.split( '[' );
			id = id[0].replace( /_/g, '-' );

			$( this ).attr( 'id', id );
		});

		// Settings: Toggle fields based on the "Show Trailer Episodes" field.
		$( '#aiopod-podcast-settings tr.show_trailer_episodes input[type=checkbox]' ).on( 'change', function() {			
			if ( this.checked ) {
				$( '#aiopod-podcast-settings' ).removeClass( 'aiopod-trailer_episodes-disabled' );
			} else {
				$( '#aiopod-podcast-settings' ).addClass( 'aiopod-trailer_episodes-disabled' );
			}
		}).trigger( 'change' );

		// Settings: Toggle fields based on the "Show Bonus Episodes" field.
		$( '#aiopod-podcast-settings tr.show_bonus_episodes input[type=checkbox]' ).on( 'change', function() {			
			if ( this.checked ) {
				$( '#aiopod-podcast-settings' ).removeClass( 'aiopod-bonus_episodes-disabled' );
			} else {
				$( '#aiopod-podcast-settings' ).addClass( 'aiopod-bonus_episodes-disabled' );
			}
		}).trigger( 'change' );
		
		// Settings: Toggle fields based on the selected episodes layout.
		$( '#aiopod-episodes-settings tr.layout select' ).on( 'change', function() {			
			var value = $( this ).val();			
			$( '#aiopod-episodes-settings' ).aiopodReplaceClass( /\aiopod-layout-\S+/ig, 'aiopod-layout-' + value );
		}).trigger( 'change' );

		// Settings: Toggle fields based on the selected categories layout.
		$( '#aiopod-categories-settings tr.layout select' ).on( 'change', function() {			
			var value = $( this ).val();			
			$( '#aiopod-categories-settings' ).aiopodReplaceClass( /\aiopod-layout-\S+/ig, 'aiopod-layout-' + value );
		}).trigger( 'change' );

		// Settings: Toggle fields based on the selected tags layout.
		$( '#aiopod-tags-settings tr.layout select' ).on( 'change', function() {			
			var value = $( this ).val();			
			$( '#aiopod-tags-settings' ).aiopodReplaceClass( /\aiopod-layout-\S+/ig, 'aiopod-layout-' + value );
		}).trigger( 'change' );	
		
		// Settings: Toggle fields based on the "Restricted Access Label" field.
		$( '#aiopod-restrictions-settings tr.show_restricted_label input[type=checkbox]' ).on( 'change', function() {			
			if ( this.checked ) {
				$( '#aiopod-restrictions-settings' ).removeClass( 'aiopod-restricted_label-disabled' );
			} else {
				$( '#aiopod-restrictions-settings' ).addClass( 'aiopod-restricted_label-disabled' );
			}
		}).trigger( 'change' );

		// Shortcode Builder: Toggle shortcode forms.
		$( '#aiopod-shortcode-selector' ).on( 'change', function() {
			var value = $( this ).val();

			$( '.aiopod-shortcode-form' ).hide();
			$( '#aiopod-shortcode-form-' + value ).show();

			$( '.aiopod-shortcode-instructions' ).hide();			
			$( '#aiopod-shortcode-instructions-' + value ).show();
		}).trigger( 'change' );				

		// Shortcode Builder: Toggle fields based on the "Select Layout" field in the episodes form.
		$( '#aiopod-shortcode-form-episodes select[name=layout]' ).on( 'change', function() {			
			var value = $( this ).val();			
			$( '#aiopod-shortcode-form-episodes' ).aiopodReplaceClass( /\aiopod-layout-\S+/ig, 'aiopod-layout-' + value );
		}).trigger( 'change' );

		// Shortcode Builder: Toggle fields based on the "Enable Player" field in the episodes form.
		$( '#aiopod-shortcode-form-episodes input[name=enable_player]' ).on( 'change', function() {			
			if ( this.checked ) {
				$( '#aiopod-shortcode-form-episodes' ).removeClass( 'aiopod-player-disabled' );
			} else {
				$( '#aiopod-shortcode-form-episodes' ).addClass( 'aiopod-player-disabled' );
			}
		}).trigger( 'change' );

		// Shortcode Builder: Toggle fields based on the "Enable Filters" field in the episodes form.
		$( '#aiopod-shortcode-form-episodes input[name=enable_filters]' ).on( 'change', function() {			
			if ( this.checked ) {
				$( '#aiopod-shortcode-form-episodes' ).removeClass( 'aiopod-filters-disabled' );
			} else {
				$( '#aiopod-shortcode-form-episodes' ).addClass( 'aiopod-filters-disabled' );
			}
		}).trigger( 'change' );

		// Shortcode Builder: Toggle fields based on the "Select Layout" field in the categories form.
		$( '#aiopod-shortcode-form-categories select[name=layout]' ).on( 'change', function() {			
			var value = $( this ).val();			
			$( '#aiopod-shortcode-form-categories' ).aiopodReplaceClass( /\aiopod-layout-\S+/ig, 'aiopod-layout-' + value );
		}).trigger( 'change' );

		// Shortcode Builder: Toggle fields based on the "Select Layout" field in the tags form.
		$( '#aiopod-shortcode-form-tags select[name=layout]' ).on( 'change', function() {			
			var value = $( this ).val();			
			$( '#aiopod-shortcode-form-tags' ).aiopodReplaceClass( /\aiopod-layout-\S+/ig, 'aiopod-layout-' + value );
		}).trigger( 'change' );

		// Shortcode Builder: Generate shortcode.
		$( '#aiopod-generate-shortcode' ).on( 'click', function( event ) { 
			event.preventDefault();			

			// Shortcode
			var shortcode = $( '#aiopod-shortcode-selector' ).val();

			// Build attributes.
			var attributes = shortcode;
			var obj = {};
			
			$( '.aiopod-shortcode-field', '#aiopod-shortcode-form-' + shortcode ).each(function() {							
				var $this = $( this );
				var type  = $this.attr( 'type' );
				var name  = $this.attr( 'name' );				
				var value = $this.val();
				var def   = 0;
				
				if ( typeof $this.data( 'default' ) !== 'undefined' ) {
					def = $this.data( 'default' );
				}				
				
				// Is checkbox?
				if ( type == 'checkbox' ) {
					value = $this.is( ':checked' ) ? 1 : 0;
				} else {
					// Is category or tag?
					if ( name == 'categories' || name == 'tags' || name == 'participants' ) {					
						value = $this.find( 'input[type=checkbox]:checked' ).map(function() {
							return this.value;
						}).get().join( ',' );
					}
				}
				
				// Add only if the user input differ from the global configuration.
				if ( value != def ) {
					obj[ name ] = value;
				}				
			});
			
			for ( var name in obj ) {
				if ( obj.hasOwnProperty( name ) ) {
					attributes += ( ' ' + name + '="' + obj[ name ] + '"' );
				}
			}

			// Shortcode output.	
			$( '#aiopod-shortcode').val( '[aiopod_' + attributes + ']' ); 
		});

		// Shortcode Builder: Generate shortcode.
		$( '#aiopod-shortcode-modal .aiopod-button-copy' ).on( 'click', function( event ) { 
			event.preventDefault();		

			const input = document.querySelector( '#aiopod-shortcode' );

			input.focus();
			input.select();

			document.execCommand( 'copy' );

			alert( aiopod_admin.i18n.copied );
		});

		// Episodes Widget: Toggle fields based on the "Select Layout" field.
		$( document ).on( 'change', '.aiopod-widget-form-episodes .aiopod-widget-field-layout', function() {			
			var value = $( this ).val();			
			$( this ).closest( '.aiopod-widget-form-episodes' ).aiopodReplaceClass( /\aiopod-layout-\S+/ig, 'aiopod-layout-' + value );
		});

		// Episodes Widget: Toggle fields based on the "Enable Player" field.
		$( document ).on( 'change', '.aiopod-widget-form-episodes .aiopod-widget-field-enable_player', function() {			
			if ( this.checked ) {
				$( this ).closest( '.aiopod-widget-form-episodes' ).removeClass( 'aiopod-player-disabled' );
			} else {
				$( this ).closest( '.aiopod-widget-form-episodes' ).addClass( 'aiopod-player-disabled' );
			}			
		});

		// Episodes Widget: Toggle fields based on the "Enable Filters" field.
		$( document ).on( 'change', '.aiopod-widget-form-episodes .aiopod-widget-field-enable_filters', function() {			
			if ( this.checked ) {
				$( this ).closest( '.aiopod-widget-form-episodes' ).removeClass( 'aiopod-filters-disabled' );
			} else {
				$( this ).closest( '.aiopod-widget-form-episodes' ).addClass( 'aiopod-filters-disabled' );
			}			
		});

		// Categories Widget: Toggle fields based on the "Select Layout" field.
		$( document ).on( 'change', '.aiopod-widget-form-categories .aiopod-widget-field-layout', function() {			
			var value = $( this ).val();			
			$( this ).closest( '.aiopod-widget-form-categories' ).aiopodReplaceClass( /\aiopod-layout-\S+/ig, 'aiopod-layout-' + value );
		});

		// Tags Widget: Toggle fields based on the "Select Layout" field.
		$( document ).on( 'change', '.aiopod-widget-form-tags .aiopod-widget-field-layout', function() {			
			var value = $( this ).val();			
			$( this ).closest( '.aiopod-widget-form-tags' ).aiopodReplaceClass( /\aiopod-layout-\S+/ig, 'aiopod-layout-' + value );
		});
				   
	});	

})( jQuery );
