( function( wp ) { const { __ } = wp.i18n; const { getBlockType, registerBlockType } = wp.blocks; const { createElement: el, Fragment, useState } = wp.element; const { InspectorControls, MediaUpload, MediaUploadCheck } = wp.blockEditor; const { PanelBody, TextControl, TextareaControl, Button, ToggleControl, SelectControl, BaseControl, Notice } = wp.components; const ServerSideRender = wp.serverSideRender; const themeOptions = [ { label: __( 'Modern Dark', 'modern-audio-player' ), value: 'modern-dark' }, { label: __( 'Glassmorphism', 'modern-audio-player' ), value: 'glassmorphism' }, { label: __( 'Podcast Style', 'modern-audio-player' ), value: 'podcast-style' } ]; function getAudioUrlWarning( value ) { if ( ! value ) { return ''; } try { const url = new window.URL( value, window.location.origin ); const hostname = url.hostname.toLowerCase(); const pathname = url.pathname.toLowerCase(); if ( [ 'youtube.com', 'www.youtube.com', 'youtu.be', 'soundcloud.com', 'open.spotify.com', 'music.apple.com' ].includes( hostname ) ) { return __( 'This URL looks like a page, not a direct audio file. Please use a direct MP3 or other audio file URL.', 'modern-audio-player' ); } if ( /\.(html?|php)$/i.test( pathname ) ) { return __( 'This URL does not look like a direct audio file. Please confirm it points directly to the audio asset.', 'modern-audio-player' ); } } catch ( error ) { return __( 'Please enter a valid audio URL.', 'modern-audio-player' ); } return ''; } function formatPlaylistText( playlist ) { if ( ! Array.isArray( playlist ) || ! playlist.length ) { return ''; } return playlist.map( function( track ) { return [ track.title || '', track.src || '', track.image || '' ].join( ' | ' ); } ).join( '\n' ); } function parsePlaylistText( value ) { return String( value || '' ) .split( /\r\n|\r|\n/ ) .map( function( line ) { return line.trim(); } ) .filter( function( line ) { return line.length > 0; } ) .map( function( line ) { const parts = line.split( '|' ).map( function( part ) { return part.trim(); } ); return { title: parts[ 0 ] || '', src: parts[ 1 ] || '', image: parts[ 2 ] || '' }; } ) .filter( function( track ) { return !! track.src; } ); } const blockSettings = { edit: function( props ) { const attributes = props.attributes; const setAttributes = props.setAttributes; const initialPlaylistText = formatPlaylistText( attributes.playlist || [] ); const playlistState = useState( initialPlaylistText ); const playlistText = playlistState[ 0 ]; const setPlaylistText = playlistState[ 1 ]; const firstPlaylistTrack = Array.isArray( attributes.playlist ) && attributes.playlist.length ? attributes.playlist[ 0 ] : null; const audioUrlWarning = getAudioUrlWarning( attributes.src || ( firstPlaylistTrack && firstPlaylistTrack.src ) || '' ); function onSelectAudio( media ) { setAttributes( { src: media && media.url ? media.url : '', title: attributes.title || ( media && media.title ? media.title : '' ) } ); } function onSelectImage( media ) { setAttributes( { image: media && media.url ? media.url : '' } ); } return el( Fragment, {}, el( InspectorControls, {}, el( PanelBody, { title: __( 'Player Content', 'modern-audio-player' ), initialOpen: true }, el( TextControl, { label: __( 'Track Title', 'modern-audio-player' ), value: attributes.title || '', onChange: function( value ) { setAttributes( { title: value } ); } } ), el( TextControl, { label: __( 'Audio Source URL', 'modern-audio-player' ), value: attributes.src || '', help: __( 'Paste a direct audio URL or choose one from the media library below. This remains the fallback single-track source.', 'modern-audio-player' ), onChange: function( value ) { setAttributes( { src: value } ); } } ), audioUrlWarning ? el( Notice, { status: 'warning', isDismissible: false }, audioUrlWarning ) : null, el( BaseControl, { label: __( 'Audio File', 'modern-audio-player' ) }, el( MediaUploadCheck, {}, el( MediaUpload, { onSelect: onSelectAudio, allowedTypes: [ 'audio' ], render: function( renderProps ) { return el( Button, { variant: 'secondary', onClick: renderProps.open }, attributes.src ? __( 'Replace Audio File', 'modern-audio-player' ) : __( 'Choose Audio File', 'modern-audio-player' ) ); } } ) ) ), el( BaseControl, { label: __( 'Cover Image', 'modern-audio-player' ) }, el( MediaUploadCheck, {}, el( MediaUpload, { onSelect: onSelectImage, allowedTypes: [ 'image' ], render: function( renderProps ) { return el( Button, { variant: 'secondary', onClick: renderProps.open }, attributes.image ? __( 'Replace Cover Image', 'modern-audio-player' ) : __( 'Choose Cover Image', 'modern-audio-player' ) ); } } ) ) ), el( TextareaControl, { label: __( 'Playlist Tracks', 'modern-audio-player' ), help: __( 'One track per line in this format: Title | Audio URL | Image URL. If empty, the single-track fields above are used.', 'modern-audio-player' ), value: playlistText, onChange: function( value ) { setPlaylistText( value ); setAttributes( { playlist: parsePlaylistText( value ) } ); } } ) ), el( PanelBody, { title: __( 'Theme', 'modern-audio-player' ), initialOpen: false }, el( ToggleControl, { label: __( 'Inherit Global Theme', 'modern-audio-player' ), checked: !! attributes.useGlobalTheme, onChange: function( value ) { setAttributes( { useGlobalTheme: value } ); } } ), el( SelectControl, { label: __( 'Theme Override', 'modern-audio-player' ), value: attributes.theme || 'modern-dark', options: themeOptions, disabled: !! attributes.useGlobalTheme, onChange: function( value ) { setAttributes( { theme: value } ); } } ) ) ), el( 'div', { className: 'map-editor-preview' }, el( ServerSideRender, { block: props.name, attributes: attributes } ) ) ); }, save: function() { return null; } }; if ( ! getBlockType( 'velora/audio-player' ) ) { registerBlockType( 'velora/audio-player', blockSettings ); } if ( ! getBlockType( 'map/audio-player' ) ) { registerBlockType( 'map/audio-player', blockSettings ); } } )( window.wp );