> $blocks Parsed blocks. * @param array $context Render context. * @return string */ public function render( $blocks, $context ) { $attributes = $context['attributes']; $instance_id = $context['instance_id']; $library = $context['library']; $has_diagram = 'true' === $attributes['diagrams']; $show_print_button = 'true' === ( $attributes['print_button'] ?? 'true' ); $default_scale = isset( $context['settings']['default_song_scale'] ) ? (float) $context['settings']['default_song_scale'] : 1; $diagram_width = isset( $context['settings']['default_diagram_width'] ) ? (int) $context['settings']['default_diagram_width'] : 20; $chord_font_size = isset( $context['settings']['chord_prompt_font_size'] ) ? (int) $context['settings']['chord_prompt_font_size'] : 14; $block_spacing = isset( $context['settings']['block_spacing'] ) ? (int) $context['settings']['block_spacing'] : 12; $section_spacing = isset( $context['settings']['section_break_spacing'] ) ? (int) $context['settings']['section_break_spacing'] : 24; $song_classes = $has_diagram ? 'da-plectrum-song--has-diagrams' : 'da-plectrum-song--names-only'; $song_classes = 'da-plectrum-song ' . $song_classes . ' da-plectrum-song--interactive da-plectrum-song--printable'; ob_start(); /** * Fires before a rendered song. * * @param string $instance_id Song instance ID. * @param array $context Rendering context. */ do_action( 'da_plectrum_before_song', $instance_id, $context ); ?>
render_controls( $has_diagram, $show_print_button ); ?>

render_blocks( $blocks, $library, $attributes ); ?>
> $blocks Parsed blocks. * @param array> $library Chord library. * @param array $attributes Shortcode attributes. * @return void */ private function render_blocks( $blocks, $library, $attributes ) { $pending_breaks = 0; $is_first_block = true; foreach ( $blocks as $block ) { if ( 'spacer' === $block['type'] ) { ++$pending_breaks; continue; } $this->render_block( $block, $library, $attributes, $pending_breaks, $is_first_block ); $pending_breaks = 0; $is_first_block = false; } } /** * Render a parsed block. * * @param array $block Parsed block. * @param array> $library Chord library. * @param array $attributes Shortcode attributes. * @param int $pending_breaks Number of blank-line spacers before this block. * @param bool $is_first_block Whether this is the first rendered content block. * @return void */ private function render_block( $block, $library, $attributes, $pending_breaks = 0, $is_first_block = false ) { $classes = array( 'da-plectrum-section' ); if ( 'html-block' === $block['type'] ) { $classes[] = 'da-plectrum-section--html'; } else { $classes[] = 'da-plectrum-section--paragraph'; } if ( ! $is_first_block ) { $classes[] = $pending_breaks > 0 ? 'da-plectrum-section--after-break' : 'da-plectrum-section--adjacent'; } $spacing_multiplier = max( 1, (int) $pending_breaks ); $inline_style = ''; if ( ! $is_first_block && $pending_breaks > 0 ) { $inline_style = '--da-plectrum-break-multiplier: ' . $spacing_multiplier . ';'; } echo '
'; if ( 'html-block' === $block['type'] ) { foreach ( $block['lines'] as $line ) { $this->render_line( $line, $library, $attributes ); } echo '
'; return; } foreach ( $block['lines'] as $line ) { $this->render_line( $line, $library, $attributes ); } echo ''; } /** * Render controls. * * @param bool $has_diagram Whether diagrams are enabled for the current song. * @param bool $show_print_button Whether to render the print control. * @return void */ private function render_controls( $has_diagram, $show_print_button ) { ob_start(); ?>
array( 'class' => true, 'role' => true, 'aria-label' => true, ), 'span' => array( 'class' => true, 'aria-hidden' => true, ), 'button' => array( 'class' => true, 'type' => true, 'data-da-plectrum-action' => true, 'aria-pressed' => true, 'aria-label' => true, ), ) ); } /** * Render a parsed line. * * @param array $line Parsed line. * @param array> $library Chord library. * @param array $attributes Shortcode attributes. * @return void */ private function render_line( $line, $library, $attributes ) { $line_class = 'da-plectrum-line'; if ( 'blank' === $line['type'] ) { echo ''; return; } $line_class .= 'chords-only' === $line['type'] ? ' da-plectrum-line--chords-only' : ' da-plectrum-line--lyrics'; ?>
render_segment( $segment, $library, $attributes ); ?>
$segment Segment data. * @param array> $library Chord library. * @param array $attributes Shortcode attributes. * @return void */ private function render_segment( $segment, $library, $attributes ) { if ( 'html' === $segment['type'] ) { echo wp_kses_post( $segment['lyric'] ); return; } $chord = $segment['chord']; $lyric = $segment['lyric']; $has_chord = null !== $chord; $chord_parts = $has_chord ? $this->parse_chord_parts( $chord ) : null; $chord_data = $has_chord ? $this->find_chord_data( $chord, $library ) : null; $voicing = ! empty( $chord_data['voicings'][0] ) ? $chord_data['voicings'][0] : null; $segment_css = $has_chord ? 'da-plectrum-segment da-plectrum-segment--with-chord' : 'da-plectrum-segment da-plectrum-segment--lyrics-only'; $segment_css .= ( $has_chord && empty( $chord_data ) ) ? ' da-plectrum-segment--unknown-chord' : ''; $diagram_markup = ''; if ( $has_chord && 'true' === $attributes['diagrams'] ) { $diagram_markup = $this->render_diagram_markup( $chord, $chord_data ); } ?> data-da-plectrum-chord-original="" data-da-plectrum-chord-current="" data-da-plectrum-root="" data-da-plectrum-quality="" data-da-plectrum-bass="" data-da-plectrum-known="" data-da-plectrum-voicing="" > render_chord_name_markup( $chord, $chord_data, $segment, $attributes ), array( 'span' => array( 'class' => true ) ) ); ?> get_diagram_allowed_html() ); ?> > */ private function get_diagram_allowed_html() { return array( 'svg' => array( 'class' => true, 'viewbox' => true, 'role' => true, 'aria-labelledby' => true, 'aria-label' => true, ), 'title' => array( 'id' => true ), 'path' => array( 'd' => true ), 'circle' => array( 'cx' => true, 'cy' => true, 'r' => true, 'fill' => true, ), 'line' => array( 'x1' => true, 'y1' => true, 'x2' => true, 'y2' => true, 'stroke-width' => true, ), 'text' => array( 'class' => true, 'x' => true, 'y' => true, 'text-anchor' => true, ), ); } /** * Render a simple inline SVG diagram placeholder from chord data. * * @param string $chord Chord name. * @param array $chord_data Chord data. * @return void */ private function render_diagram_placeholder( $chord, $chord_data ) { $voicing = $chord_data['voicings'][0] ?? array(); $frets = $voicing['frets'] ?? array(); /* translators: %s: Chord name shown in the SVG diagram title. */ $label = sprintf( __( '%s ukulele chord diagram', 'plectrum' ), $chord ); ?> $fret ) : ?> 0 ) : ?> |null $chord_data Chord data. * @param array $segment Segment context. * @param array $attributes Render attributes. * @return string */ private function render_chord_name_markup( $chord, $chord_data, $segment, $attributes ) { $markup = sprintf( '%s', empty( $chord_data ) ? ' da-plectrum-chord--unknown' : '', esc_html( $chord ) ); return (string) apply_filters( 'da_plectrum_render_chord', $markup, $chord, $chord_data, $segment, $attributes ); } /** * Render filterable diagram markup. * * @param string $chord Chord text. * @param array|null $chord_data Chord data. * @return string */ private function render_diagram_markup( $chord, $chord_data ) { if ( empty( $chord_data ) ) { return (string) apply_filters( 'da_plectrum_render_diagram', '', $chord, $chord_data ); } ob_start(); $this->render_diagram_placeholder( $chord, $chord_data ); return (string) apply_filters( 'da_plectrum_render_diagram', (string) ob_get_clean(), $chord, $chord_data ); } /** * Replace supported placeholders in the footer notice. * * @param string $notice Footer notice template. * @param array $context Render context. * @return string */ private function render_footer_notice( $notice, $context ) { $post_context = $context['post'] ?? array(); $replacements = array( '{year}' => (string) ( $post_context['year'] ?? gmdate( 'Y' ) ), '{site_name}' => (string) ( $post_context['site_name'] ?? '' ), '{post_title}' => (string) ( $post_context['title'] ?? '' ), '{author}' => (string) ( $post_context['author'] ?? '' ), ); $resolved = strtr( $notice, $replacements ); /** * Filter the resolved print footer notice. * * @param string $resolved Resolved footer text. * @param string $notice Original footer template. * @param array $context Render context. */ return (string) apply_filters( 'da_plectrum_print_footer', $resolved, $notice, $context ); } /** * Parse chord parts for render metadata. * * @param string $chord Chord text. * @return array|null */ private function parse_chord_parts( $chord ) { if ( ! preg_match( '/^(?P[A-G](?:#|b)?)(?P.*?)(?:\/(?P[A-G](?:#|b)?))?$/', $chord, $matches ) ) { return null; } return array( 'root' => $matches['root'], 'quality' => $matches['quality'], 'bass' => $matches['bass'] ?? '', ); } /** * Find chord data by chord name or alias. * * @param string $chord Chord name. * @param array> $library Chord library. * @return array|null */ private function find_chord_data( $chord, $library ) { $normalized = strtolower( preg_replace( '/\s+/', '', $chord ) ); foreach ( $library as $entry ) { if ( strtolower( $entry['name'] ) === $normalized ) { return $entry; } foreach ( $entry['aliases'] as $alias ) { if ( strtolower( $alias ) === $normalized ) { return $entry; } } } return null; } }