/** * Copyright 2019-2024 Nicolas Jonas * License: GPL 3.0 * * Based on: https://gist.github.com/pento/cf38fd73ce0f13fcf0f0ae7d6c4b685d * Copyright 2019 Gary Pendergast * License: GPL 2.0+ */ import json from './block.json'; import './editor.scss'; import { __ } from '@wordpress/i18n'; import ServerSideRender from '@wordpress/server-side-render'; import { MediaUpload, MediaUploadCheck, InspectorControls, useBlockProps, } from '@wordpress/block-editor'; import { BaseControl, TextControl, Button, ToggleControl, SelectControl, PanelBody, ResponsiveWrapper, DropZone, } from '@wordpress/components'; import { registerBlockType } from '@wordpress/blocks'; import classnames from 'classnames'; import { Fragment } from '@wordpress/element'; export {}; declare global { interface Window { ArveBlockJsBefore: inlineScriptJSON; Sanitizer?: any; } } interface inlineScriptJSON { settings: Record< string, OptionProps >; options: Record< string, boolean | number | string >; } interface sectionControls { main: Array< JSX.Element >; pro: Array< JSX.Element >; html5: Array< JSX.Element >; sticky_videos: Array< JSX.Element >; random_video: Array< JSX.Element >; privacy: Array< JSX.Element >; } interface SelectOption { label: string; value: string; disabled?: boolean; } interface OptionProps { label: string; tab: string; type: string; description?: string; placeholder?: string; options?: Record< string, string >; ui?: 'image_upload'; ui_element: 'select' | 'input'; ui_element_type: 'text' | 'number' | 'checkbox'; } const { name } = json; const { settings, options } = window.ArveBlockJsBefore; delete settings?.align?.options?.center; const domParser = new DOMParser(); /** * Keypair to Gutenberg component * @param selectOptions */ function PrepareSelectOptions( selectOptions: Record< string, string > | undefined ): Array< SelectOption > { if ( ! selectOptions ) { throw new Error( 'no options' ); } return Object.entries( selectOptions ).map( ( [ key, value ] ) => ( { label: value, value: key, } ) ); } function changeTextControl( key: string, value: string, props ) { if ( 'url' === key ) { const iframe = domParser.parseFromString( value, 'text/html' ).querySelector( 'iframe' ); if ( iframe && iframe.getAttribute( 'src' ) ) { props.setAttributes( { [ key ]: iframe.getAttribute( 'src' ), } ); if ( iframe.width && iframe.height ) { const ratio = aspectRatio( iframe.width, iframe.height ); if ( '16:9' !== ratio ) { props.setAttributes( { aspect_ratio: ratio, } ); } } return; } } props.setAttributes( { [ key ]: value, } ); } const mediaUploadRender = ( open: VoidFunction, val, url: string ): JSX.Element => { return (
{ /* @ts-ignore */ }
); }; function buildControls( props ) { const controls = [] as Array< JSX.Element >; const sectionControls = {} as sectionControls; const mediaUploadInstructions = (

{ __( 'To edit the featured image, you need permission to upload media.' ) }

); let selectedMedia; Object.values( settings ).forEach( ( option: OptionProps ) => { sectionControls[ option.tab ] = []; } ); Object.entries( settings ).forEach( ( [ key, option ]: [ string, OptionProps ] ) => { const val = props.attributes[ key ]; const url = ''; sectionControls[ option.tab ].push( { 'select' === option.ui_element && ( { return props.setAttributes( { [ key ]: '' === value ? undefined : value, } ); } } /> ) } { 'checkbox' === option.ui_element_type && ( { return props.setAttributes( { [ key ]: value, } ); } } /> ) } { [ 'text', 'number' ].includes( option.ui_element_type ) && ( { changeTextControl( key, value, props ); } } /> ) } { 'image_upload' === option.ui && ( { selectedMedia = media; return props.setAttributes( { [ key ]: media.id.toString(), [ key + '_url' ]: media.url, } ); } } allowedTypes={ [ 'image' ] } modalClass="editor-post-featured-image__media-modal" render={ ( { open } ) => { return mediaUploadRender( open, val, url ); } } value={ val } /> { !! val && !! url && ( { selectedMedia = media; return props.setAttributes( { [ key ]: media.id.toString(), [ key + '_url' ]: media.url, } ); } } allowedTypes={ [ 'image' ] } modalClass="editor-post-featured-image__media-modal" render={ ( { open } ) => ( ) } /> ) } { !! val && ( ) } ) } ); } ); sectionControls.main.push( { __( 'Info', 'advanced-responsive-video-embedder' ) } ); Object.keys( sectionControls ).forEach( ( key ) => { controls.push( { sectionControls[ key ] } ); } ); return controls; // Object.keys( sectionControls ).forEach( ( key ) => { // controls.push( sectionControls[ key ] ); // open = false; // } ); // return ( // // { controls } // // ); } function createHelp( description: string | undefined ): string | JSX.Element { if ( ! description ) { return ''; } const doc = domParser.parseFromString( description, 'text/html' ); const link = doc.querySelector( 'a' ); if ( link ) { const href = link.getAttribute( 'href' ) || ''; const linkText = link.textContent || ''; description = doc.body.textContent || ''; const textSplit = description.split( linkText ); if ( textSplit.length !== 2 ) { throw new Error( 'textSplit.length must be 2' ); } return ( <> { textSplit[ 0 ] } { linkText } { textSplit[ 1 ] } ); } return description; } function capitalizeFirstLetter( str: string ): string { return str.charAt( 0 ).toUpperCase() + str.slice( 1 ); } function Edit( props: Record< string, any > ) { const { attributes: { mode, align, maxwidth }, } = props; let pointerEvents = true; const style = {} as React.CSSProperties; const attrCopy = JSON.parse( JSON.stringify( props.attributes ) ); delete attrCopy.align; delete attrCopy.maxwidth; if ( maxwidth && ( 'left' === align || 'right' === align ) ) { style.width = '100%'; style.maxWidth = maxwidth; } else if ( 'left' === align || 'right' === align ) { style.width = '100%'; style.maxWidth = options.align_maxwidth as string | number; } const blockProps = useBlockProps( { style } ); if ( 'normal' === mode || ( ! mode && 'normal' === options.mode ) ) { pointerEvents = false; } return ( <>
{ buildControls( props ) } ); } // @ts-ignore registerBlockType( name, { edit: Edit, } ); /** * Calculate aspect ratio based on width and height. * * @param {string} width - The width value * @param {string} height - The height value * @return {string} The aspect ratio in the format 'width:height' */ function aspectRatio( width: string, height: string ): string { if ( isIntOverZero( width ) && isIntOverZero( height ) ) { const w = parseInt( width ); const h = parseInt( height ); const arGCD = gcd( w, h ); return w / arGCD + ':' + h / arGCD; } return width + ':' + height; } /** * Check if the input string is a positive integer. * * @param {string} str - The input string to be checked. * @return {boolean} Whether the input string is a positive integer or not. */ function isIntOverZero( str: string ): boolean { const n = Math.floor( Number( str ) ); return n !== Infinity && String( n ) === str && n > 0; } /** * Calculates the greatest common divisor of two numbers using the Euclidean algorithm. * * @param {number} a - the first number * @param {number} b - the second number * @return {number} the greatest common divisor of the two numbers */ function gcd( a: number, b: number ): number { if ( ! b ) { return a; } return gcd( b, a % b ); } /* TODO when the sanitizer API function sanitizeHelpHTML( option: OptionProps ) { if ( typeof option.description !== 'string' ) { return ''; } const div = document.createElement( 'div' ); if ( 'Sanitizer' in window ) { const sanitizer = new window.Sanitizer( { allowElements: [ 'a' ], allowAttributes: { target: [ 'a' ], href: [ 'a' ], }, } ); // @ts-ignore div.setHTML( option.description, { sanitizer } ); return ; } return stripHTML( option.description ); } function stripHTML( html ) { const doc = new DOMParser().parseFromString( html, 'text/html' ); return doc.body.textContent || ''; } */