Testimonails #1
$0.00
<?php
/**
* Free Section Code Viewer
*
* A production-ready, VS Code style "Free Code Viewer" for WordPress +
* WooCommerce + ACF Pro + Elementor (WoodMart Child Theme).
*
* Add this file's contents to your WoodMart Child Theme's functions.php,
* or require it from functions.php, e.g.:
* require_once get_stylesheet_directory() . '/inc/free-section-code-viewer.php';
*
* Place the companion assets at:
* /assets/css/code-viewer.css
* /assets/js/code-viewer.js
* (relative to the child theme root)
*
* ACF Field: section_code (Text Area / Code field on the Product post type)
* Shortcode: [free_section_code]
*
* @package WoodMart_Child
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Class FSC_Free_Section_Code_Viewer
*
* Handles rendering of the [free_section_code] shortcode and conditional
* asset loading (CSS/JS only enqueued on pages/products that actually use it).
*/
final class FSC_Free_Section_Code_Viewer {
/**
* Singleton instance.
*
* @var FSC_Free_Section_Code_Viewer|null
*/
private static $instance = null;
/**
* Shortcode tag.
*
* @var string
*/
const SHORTCODE_TAG = 'free_section_code';
/**
* ACF field name used to store the raw code.
*
* @var string
*/
const FIELD_NAME = 'section_code';
/**
* Nonce action name (reserved for future AJAX use / verification).
*
* @var string
*/
const NONCE_ACTION = 'fsc_code_viewer_nonce';
/**
* Whether the current request has been detected as needing our assets.
*
* @var bool
*/
private $should_load_assets = false;
/**
* Whether assets have already been enqueued (avoid double enqueue).
*
* @var bool
*/
private static $assets_enqueued = false;
/**
* Whether the inline fallback (style/script) has already been printed.
* Used only as a safety net when detection on the `wp` hook misses
* dynamically built content (e.g. some Elementor contexts).
*
* @var bool
*/
private static $inline_fallback_printed = false;
/**
* Get singleton instance.
*
* @return FSC_Free_Section_Code_Viewer
*/
public static function instance() {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Constructor. Hooks everything up.
*/
private function __construct() {
add_shortcode( self::SHORTCODE_TAG, array( $this, 'render_shortcode' ) );
add_action( 'wp', array( $this, 'detect_shortcode_usage' ) );
add_action( 'wp_enqueue_scripts', array( $this, 'maybe_enqueue_assets' ) );
}
/**
* Detect, before wp_head fires, whether the current request will output
* our shortcode. This keeps CSS/JS out of pages that don't need it.
*
* Checks:
* - The main queried post's post_content (classic editor / WooCommerce description).
* - Elementor's `_elementor_data` meta (Shortcode Widget content on Elementor built pages).
*
* @return void
*/
public function detect_shortcode_usage() {
if ( ! is_singular() ) {
return;
}
$post_id = get_queried_object_id();
if ( ! $post_id ) {
return;
}
$needle = '[' . self::SHORTCODE_TAG;
// 1) Classic content / WooCommerce short & long description.
$post = get_post( $post_id );
if ( $post instanceof WP_Post && has_shortcode( (string) $post->post_content, self::SHORTCODE_TAG ) ) {
$this->should_load_assets = true;
return;
}
// WooCommerce short description.
$excerpt = get_post_field( 'post_excerpt', $post_id );
if ( $excerpt && has_shortcode( (string) $excerpt, self::SHORTCODE_TAG ) ) {
$this->should_load_assets = true;
return;
}
// 2) Elementor page builder data (Shortcode Widget stores raw text in meta).
$elementor_data = get_post_meta( $post_id, '_elementor_data', true );
if ( is_string( $elementor_data ) && false !== strpos( $elementor_data, $needle ) ) {
$this->should_load_assets = true;
return;
}
/**
* Allow developers to force-load assets in edge cases where automatic
* detection is not sufficient (e.g. shortcode injected via a widget
* area, theme builder header/footer, or a third-party page builder).
*/
if ( apply_filters( 'fsc_force_load_assets', false, $post_id ) ) {
$this->should_load_assets = true;
}
}
/**
* Register + conditionally enqueue the CSS/JS assets.
*
* @return void
*/
public function maybe_enqueue_assets() {
if ( ! $this->should_load_assets ) {
return;
}
$this->enqueue_assets();
}
/**
* Actually register/enqueue the CSS and JS files.
*
* Safe to call multiple times; only enqueues once per request.
*
* @return void
*/
private function enqueue_assets() {
if ( self::$assets_enqueued ) {
return;
}
$css_rel_path = '/assets/css/code-viewer.css';
$js_rel_path = '/assets/js/code-viewer.js';
$css_path = get_stylesheet_directory() . $css_rel_path;
$js_path = get_stylesheet_directory() . $js_rel_path;
$css_version = file_exists( $css_path ) ? filemtime( $css_path ) : '1.0.0';
$js_version = file_exists( $js_path ) ? filemtime( $js_path ) : '1.0.0';
wp_enqueue_style(
'fsc-code-viewer-style',
get_stylesheet_directory_uri() . $css_rel_path,
array(),
$css_version
);
wp_enqueue_script(
'fsc-code-viewer-script',
get_stylesheet_directory_uri() . $js_rel_path,
array(),
$js_version,
true // Load in footer.
);
wp_script_add_data( 'fsc-code-viewer-script', 'defer', true );
self::$assets_enqueued = true;
}
/**
* Fetch the raw code value from ACF (with a safe fallback to post meta
* if ACF is temporarily unavailable).
*
* @param int $post_id Post/Product ID.
* @return string
*/
private function get_code_value( $post_id ) {
$value = '';
if ( function_exists( 'get_field' ) ) {
$value = get_field( self::FIELD_NAME, $post_id );
} else {
$value = get_post_meta( $post_id, self::FIELD_NAME, true );
}
if ( ! is_string( $value ) ) {
$value = '';
}
// Normalize line endings.
$value = str_replace( array( "\r\n", "\r" ), "\n", $value );
return trim( $value );
}
/**
* Shortcode callback: [free_section_code]
*
* Attributes:
* product_id (int) Optional. Defaults to the current post/product ID.
* filename (string) Optional. Defaults to "section.liquid".
* language (string) Optional. Label shown in the header. Defaults to "Liquid".
*
* @param array $atts Shortcode attributes.
* @return string Escaped HTML markup.
*/
public function render_shortcode( $atts ) {
// Safety net: ensure assets are queued even if detection missed this
// context (e.g. shortcode rendered inside a widget or dynamic tag).
if ( ! self::$assets_enqueued ) {
$this->enqueue_assets();
$this->maybe_print_inline_fallback();
}
$atts = shortcode_atts(
array(
'product_id' => get_the_ID(),
'filename' => 'section.liquid',
'language' => 'Liquid',
),
$atts,
self::SHORTCODE_TAG
);
$product_id = absint( $atts['product_id'] );
$filename = sanitize_file_name( (string) $atts['filename'] );
$language = sanitize_text_field( (string) $atts['language'] );
if ( '' === $filename ) {
$filename = 'section.liquid';
}
if ( $product_id <= 0 ) {
return $this->render_empty_state();
}
$code = $this->get_code_value( $product_id );
if ( '' === $code ) {
return $this->render_empty_state();
}
return $this->render_viewer( $code, $filename, $language, $product_id );
}
/**
* Build the markup for the empty state.
*
* @return string
*/
private function render_empty_state() {
ob_start();
?>
<div class="fsc-code-viewer fsc-code-viewer--empty">
<div class="fsc-cv-empty-state">
<span class="fsc-cv-empty-icon" aria-hidden="true"><?php echo $this->get_icon_svg( 'file' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- static, trusted inline SVG. ?></span>
<p class="fsc-cv-empty-text"><?php echo esc_html__( 'No code available.', 'woodmart-child' ); ?></p>
</div>
</div>
<?php
return ob_get_clean();
}
/**
* Build the full VS Code style viewer markup.
*
* @param string $code Raw code (already trimmed / normalized).
* @param string $filename File name for header + download.
* @param string $language Language label shown in the header.
* @param int $product_id Product/Post ID (used for a unique DOM id + nonce).
* @return string
*/
private function render_viewer( $code, $filename, $language, $product_id ) {
$unique_id = 'fsc-code-' . $product_id . '-' . wp_rand( 1000, 9999 );
$nonce = wp_create_nonce( self::NONCE_ACTION );
$lines = explode( "\n", $code );
$line_count = count( $lines );
ob_start();
?>
<div
class="fsc-code-viewer"
id="<?php echo esc_attr( $unique_id ); ?>"
data-fsc-filename="<?php echo esc_attr( $filename ); ?>"
data-fsc-nonce="<?php echo esc_attr( $nonce ); ?>"
>
<div class="fsc-cv-header">
<div class="fsc-cv-file-info">
<span class="fsc-cv-icon" aria-hidden="true"><?php echo $this->get_icon_svg( 'file' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span>
<span class="fsc-cv-filename"><?php echo esc_html( $filename ); ?></span>
<?php if ( '' !== $language ) : ?>
<span class="fsc-cv-language"><?php echo esc_html( $language ); ?></span>
<?php endif; ?>
</div>
<div class="fsc-cv-actions">
<button
type="button"
class="fsc-cv-btn fsc-cv-btn--copy"
data-fsc-action="copy"
aria-label="<?php echo esc_attr__( 'Copy code to clipboard', 'woodmart-child' ); ?>"
>
<span class="fsc-cv-btn-icon fsc-cv-icon-copy" aria-hidden="true"><?php echo $this->get_icon_svg( 'copy' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span>
<span class="fsc-cv-btn-icon fsc-cv-icon-check" aria-hidden="true" hidden><?php echo $this->get_icon_svg( 'check' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span>
<span class="fsc-cv-btn-text"><?php echo esc_html__( 'Copy', 'woodmart-child' ); ?></span>
</button>
<button
type="button"
class="fsc-cv-btn fsc-cv-btn--download"
data-fsc-action="download"
aria-label="<?php echo esc_attr__( 'Download file', 'woodmart-child' ); ?>"
>
<span class="fsc-cv-btn-icon" aria-hidden="true"><?php echo $this->get_icon_svg( 'download' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span>
<span class="fsc-cv-btn-text"><?php echo esc_html__( 'Download', 'woodmart-child' ); ?></span>
</button>
</div>
</div>
<div class="fsc-cv-body">
<div class="fsc-cv-line-numbers" aria-hidden="true"><?php
for ( $i = 1; $i <= $line_count; $i++ ) {
echo '<span>' . esc_html( (string) $i ) . '</span>';
}
?></div>
<pre class="fsc-cv-pre"><code class="fsc-cv-code language-<?php echo esc_attr( strtolower( $language ) ); ?>" data-fsc-raw-code="<?php echo esc_attr( $code ); ?>"><?php echo esc_html( $code ); ?></code></pre>
</div>
</div>
<?php
return ob_get_clean();
}
/**
* Safety-net: if for some reason our detection on the `wp` hook did not
* fire (assets not queued in time for wp_head), print a minimal inline
* <link>/<script> fallback directly where the shortcode is rendered so
* the widget still works. This runs at most once per request.
*
* @return void
*/
private function maybe_print_inline_fallback() {
if ( self::$inline_fallback_printed ) {
return;
}
if ( did_action( 'wp_head' ) === 0 ) {
// wp_head hasn't fired yet, our normal enqueue will still print correctly.
self::$inline_fallback_printed = true;
return;
}
$css_path = get_stylesheet_directory() . '/assets/css/code-viewer.css';
$js_path = get_stylesheet_directory() . '/assets/js/code-viewer.js';
if ( file_exists( $css_path ) ) {
echo '<style id="fsc-code-viewer-inline-style">' . wp_strip_all_tags( file_get_contents( $css_path ) ) . '</style>'; // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_file_get_contents, WordPress.Security.EscapeOutput.OutputNotEscaped
}
if ( file_exists( $js_path ) ) {
echo '<script id="fsc-code-viewer-inline-script">' . wp_strip_all_tags( file_get_contents( $js_path ) ) . '</script>'; // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_file_get_contents, WordPress.Security.EscapeOutput.OutputNotEscaped
}
self::$inline_fallback_printed = true;
}
/**
* Return a small trusted, static inline SVG icon.
* Output is hard-coded (no user input), safe to print directly.
*
* @param string $name Icon name: file|copy|check|download.
* @return string SVG markup.
*/
private function get_icon_svg( $name ) {
$icons = array(
'file' => '<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.6"><path d="M6 2h9l5 5v15H6z"/><path d="M14 2v6h6"/></svg>',
'copy' => '<svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="9" y="9" width="12" height="12" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>',
'check' => '<svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M20 6 9 17l-5-5"/></svg>',
'download' => '<svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M12 3v12"/><path d="m7 10 5 5 5-5"/><path d="M5 21h14"/></svg>',
);
return isset( $icons[ $name ] ) ? $icons[ $name ] : '';
}
}
// Bootstrap.
FSC_Free_Section_Code_Viewer::instance();
Everything You Need To Build Better Shopify Stores
Premium quality Shopify sections built with performance, flexibility and developer experience in mind.
Shopify OS 2.0
Fully compatible with all Shopify Online Store 2.0 themes.
Drag & Drop
No coding required. Add sections directly from Theme Editor.
Theme Editor
Every setting is customizable from Shopify Theme Editor.
No App Required
Lightweight code with zero third-party app dependency.
Mobile Responsive
Looks amazing across desktop, tablet and mobile devices.
Speed Optimized
Clean and optimized code built for maximum performance.
SEO Friendly
Semantic HTML and optimized structure for search engines.
Developer Friendly
Readable Liquid, CSS and JavaScript with proper comments.
<?php
/**
* Free Section Code Viewer
*
* A production-ready, VS Code style "Free Code Viewer" for WordPress +
* WooCommerce + ACF Pro + Elementor (WoodMart Child Theme).
*
* Add this file's contents to your WoodMart Child Theme's functions.php,
* or require it from functions.php, e.g.:
* require_once get_stylesheet_directory() . '/inc/free-section-code-viewer.php';
*
* Place the companion assets at:
* /assets/css/code-viewer.css
* /assets/js/code-viewer.js
* (relative to the child theme root)
*
* ACF Field: section_code (Text Area / Code field on the Product post type)
* Shortcode: [free_section_code]
*
* @package WoodMart_Child
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Class FSC_Free_Section_Code_Viewer
*
* Handles rendering of the [free_section_code] shortcode and conditional
* asset loading (CSS/JS only enqueued on pages/products that actually use it).
*/
final class FSC_Free_Section_Code_Viewer {
/**
* Singleton instance.
*
* @var FSC_Free_Section_Code_Viewer|null
*/
private static $instance = null;
/**
* Shortcode tag.
*
* @var string
*/
const SHORTCODE_TAG = 'free_section_code';
/**
* ACF field name used to store the raw code.
*
* @var string
*/
const FIELD_NAME = 'section_code';
/**
* Nonce action name (reserved for future AJAX use / verification).
*
* @var string
*/
const NONCE_ACTION = 'fsc_code_viewer_nonce';
/**
* Whether the current request has been detected as needing our assets.
*
* @var bool
*/
private $should_load_assets = false;
/**
* Whether assets have already been enqueued (avoid double enqueue).
*
* @var bool
*/
private static $assets_enqueued = false;
/**
* Whether the inline fallback (style/script) has already been printed.
* Used only as a safety net when detection on the `wp` hook misses
* dynamically built content (e.g. some Elementor contexts).
*
* @var bool
*/
private static $inline_fallback_printed = false;
/**
* Get singleton instance.
*
* @return FSC_Free_Section_Code_Viewer
*/
public static function instance() {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Constructor. Hooks everything up.
*/
private function __construct() {
add_shortcode( self::SHORTCODE_TAG, array( $this, 'render_shortcode' ) );
add_action( 'wp', array( $this, 'detect_shortcode_usage' ) );
add_action( 'wp_enqueue_scripts', array( $this, 'maybe_enqueue_assets' ) );
}
/**
* Detect, before wp_head fires, whether the current request will output
* our shortcode. This keeps CSS/JS out of pages that don't need it.
*
* Checks:
* - The main queried post's post_content (classic editor / WooCommerce description).
* - Elementor's `_elementor_data` meta (Shortcode Widget content on Elementor built pages).
*
* @return void
*/
public function detect_shortcode_usage() {
if ( ! is_singular() ) {
return;
}
$post_id = get_queried_object_id();
if ( ! $post_id ) {
return;
}
$needle = '[' . self::SHORTCODE_TAG;
// 1) Classic content / WooCommerce short & long description.
$post = get_post( $post_id );
if ( $post instanceof WP_Post && has_shortcode( (string) $post->post_content, self::SHORTCODE_TAG ) ) {
$this->should_load_assets = true;
return;
}
// WooCommerce short description.
$excerpt = get_post_field( 'post_excerpt', $post_id );
if ( $excerpt && has_shortcode( (string) $excerpt, self::SHORTCODE_TAG ) ) {
$this->should_load_assets = true;
return;
}
// 2) Elementor page builder data (Shortcode Widget stores raw text in meta).
$elementor_data = get_post_meta( $post_id, '_elementor_data', true );
if ( is_string( $elementor_data ) && false !== strpos( $elementor_data, $needle ) ) {
$this->should_load_assets = true;
return;
}
/**
* Allow developers to force-load assets in edge cases where automatic
* detection is not sufficient (e.g. shortcode injected via a widget
* area, theme builder header/footer, or a third-party page builder).
*/
if ( apply_filters( 'fsc_force_load_assets', false, $post_id ) ) {
$this->should_load_assets = true;
}
}
/**
* Register + conditionally enqueue the CSS/JS assets.
*
* @return void
*/
public function maybe_enqueue_assets() {
if ( ! $this->should_load_assets ) {
return;
}
$this->enqueue_assets();
}
/**
* Actually register/enqueue the CSS and JS files.
*
* Safe to call multiple times; only enqueues once per request.
*
* @return void
*/
private function enqueue_assets() {
if ( self::$assets_enqueued ) {
return;
}
$css_rel_path = '/assets/css/code-viewer.css';
$js_rel_path = '/assets/js/code-viewer.js';
$css_path = get_stylesheet_directory() . $css_rel_path;
$js_path = get_stylesheet_directory() . $js_rel_path;
$css_version = file_exists( $css_path ) ? filemtime( $css_path ) : '1.0.0';
$js_version = file_exists( $js_path ) ? filemtime( $js_path ) : '1.0.0';
wp_enqueue_style(
'fsc-code-viewer-style',
get_stylesheet_directory_uri() . $css_rel_path,
array(),
$css_version
);
wp_enqueue_script(
'fsc-code-viewer-script',
get_stylesheet_directory_uri() . $js_rel_path,
array(),
$js_version,
true // Load in footer.
);
wp_script_add_data( 'fsc-code-viewer-script', 'defer', true );
self::$assets_enqueued = true;
}
/**
* Fetch the raw code value from ACF (with a safe fallback to post meta
* if ACF is temporarily unavailable).
*
* @param int $post_id Post/Product ID.
* @return string
*/
private function get_code_value( $post_id ) {
$value = '';
if ( function_exists( 'get_field' ) ) {
$value = get_field( self::FIELD_NAME, $post_id );
} else {
$value = get_post_meta( $post_id, self::FIELD_NAME, true );
}
if ( ! is_string( $value ) ) {
$value = '';
}
// Normalize line endings.
$value = str_replace( array( "\r\n", "\r" ), "\n", $value );
return trim( $value );
}
/**
* Shortcode callback: [free_section_code]
*
* Attributes:
* product_id (int) Optional. Defaults to the current post/product ID.
* filename (string) Optional. Defaults to "section.liquid".
* language (string) Optional. Label shown in the header. Defaults to "Liquid".
*
* @param array $atts Shortcode attributes.
* @return string Escaped HTML markup.
*/
public function render_shortcode( $atts ) {
// Safety net: ensure assets are queued even if detection missed this
// context (e.g. shortcode rendered inside a widget or dynamic tag).
if ( ! self::$assets_enqueued ) {
$this->enqueue_assets();
$this->maybe_print_inline_fallback();
}
$atts = shortcode_atts(
array(
'product_id' => get_the_ID(),
'filename' => 'section.liquid',
'language' => 'Liquid',
),
$atts,
self::SHORTCODE_TAG
);
$product_id = absint( $atts['product_id'] );
$filename = sanitize_file_name( (string) $atts['filename'] );
$language = sanitize_text_field( (string) $atts['language'] );
if ( '' === $filename ) {
$filename = 'section.liquid';
}
if ( $product_id <= 0 ) {
return $this->render_empty_state();
}
$code = $this->get_code_value( $product_id );
if ( '' === $code ) {
return $this->render_empty_state();
}
return $this->render_viewer( $code, $filename, $language, $product_id );
}
/**
* Build the markup for the empty state.
*
* @return string
*/
private function render_empty_state() {
ob_start();
?>
<div class="fsc-code-viewer fsc-code-viewer--empty">
<div class="fsc-cv-empty-state">
<span class="fsc-cv-empty-icon" aria-hidden="true"><?php echo $this->get_icon_svg( 'file' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- static, trusted inline SVG. ?></span>
<p class="fsc-cv-empty-text"><?php echo esc_html__( 'No code available.', 'woodmart-child' ); ?></p>
</div>
</div>
<?php
return ob_get_clean();
}
/**
* Build the full VS Code style viewer markup.
*
* @param string $code Raw code (already trimmed / normalized).
* @param string $filename File name for header + download.
* @param string $language Language label shown in the header.
* @param int $product_id Product/Post ID (used for a unique DOM id + nonce).
* @return string
*/
private function render_viewer( $code, $filename, $language, $product_id ) {
$unique_id = 'fsc-code-' . $product_id . '-' . wp_rand( 1000, 9999 );
$nonce = wp_create_nonce( self::NONCE_ACTION );
$lines = explode( "\n", $code );
$line_count = count( $lines );
ob_start();
?>
<div
class="fsc-code-viewer"
id="<?php echo esc_attr( $unique_id ); ?>"
data-fsc-filename="<?php echo esc_attr( $filename ); ?>"
data-fsc-nonce="<?php echo esc_attr( $nonce ); ?>"
>
<div class="fsc-cv-header">
<div class="fsc-cv-file-info">
<span class="fsc-cv-icon" aria-hidden="true"><?php echo $this->get_icon_svg( 'file' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span>
<span class="fsc-cv-filename"><?php echo esc_html( $filename ); ?></span>
<?php if ( '' !== $language ) : ?>
<span class="fsc-cv-language"><?php echo esc_html( $language ); ?></span>
<?php endif; ?>
</div>
<div class="fsc-cv-actions">
<button
type="button"
class="fsc-cv-btn fsc-cv-btn--copy"
data-fsc-action="copy"
aria-label="<?php echo esc_attr__( 'Copy code to clipboard', 'woodmart-child' ); ?>"
>
<span class="fsc-cv-btn-icon fsc-cv-icon-copy" aria-hidden="true"><?php echo $this->get_icon_svg( 'copy' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span>
<span class="fsc-cv-btn-icon fsc-cv-icon-check" aria-hidden="true" hidden><?php echo $this->get_icon_svg( 'check' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span>
<span class="fsc-cv-btn-text"><?php echo esc_html__( 'Copy', 'woodmart-child' ); ?></span>
</button>
<button
type="button"
class="fsc-cv-btn fsc-cv-btn--download"
data-fsc-action="download"
aria-label="<?php echo esc_attr__( 'Download file', 'woodmart-child' ); ?>"
>
<span class="fsc-cv-btn-icon" aria-hidden="true"><?php echo $this->get_icon_svg( 'download' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span>
<span class="fsc-cv-btn-text"><?php echo esc_html__( 'Download', 'woodmart-child' ); ?></span>
</button>
</div>
</div>
<div class="fsc-cv-body">
<div class="fsc-cv-line-numbers" aria-hidden="true"><?php
for ( $i = 1; $i <= $line_count; $i++ ) {
echo '<span>' . esc_html( (string) $i ) . '</span>';
}
?></div>
<pre class="fsc-cv-pre"><code class="fsc-cv-code language-<?php echo esc_attr( strtolower( $language ) ); ?>" data-fsc-raw-code="<?php echo esc_attr( $code ); ?>"><?php echo esc_html( $code ); ?></code></pre>
</div>
</div>
<?php
return ob_get_clean();
}
/**
* Safety-net: if for some reason our detection on the `wp` hook did not
* fire (assets not queued in time for wp_head), print a minimal inline
* <link>/<script> fallback directly where the shortcode is rendered so
* the widget still works. This runs at most once per request.
*
* @return void
*/
private function maybe_print_inline_fallback() {
if ( self::$inline_fallback_printed ) {
return;
}
if ( did_action( 'wp_head' ) === 0 ) {
// wp_head hasn't fired yet, our normal enqueue will still print correctly.
self::$inline_fallback_printed = true;
return;
}
$css_path = get_stylesheet_directory() . '/assets/css/code-viewer.css';
$js_path = get_stylesheet_directory() . '/assets/js/code-viewer.js';
if ( file_exists( $css_path ) ) {
echo '<style id="fsc-code-viewer-inline-style">' . wp_strip_all_tags( file_get_contents( $css_path ) ) . '</style>'; // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_file_get_contents, WordPress.Security.EscapeOutput.OutputNotEscaped
}
if ( file_exists( $js_path ) ) {
echo '<script id="fsc-code-viewer-inline-script">' . wp_strip_all_tags( file_get_contents( $js_path ) ) . '</script>'; // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_file_get_contents, WordPress.Security.EscapeOutput.OutputNotEscaped
}
self::$inline_fallback_printed = true;
}
/**
* Return a small trusted, static inline SVG icon.
* Output is hard-coded (no user input), safe to print directly.
*
* @param string $name Icon name: file|copy|check|download.
* @return string SVG markup.
*/
private function get_icon_svg( $name ) {
$icons = array(
'file' => '<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.6"><path d="M6 2h9l5 5v15H6z"/><path d="M14 2v6h6"/></svg>',
'copy' => '<svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="9" y="9" width="12" height="12" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>',
'check' => '<svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M20 6 9 17l-5-5"/></svg>',
'download' => '<svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M12 3v12"/><path d="m7 10 5 5 5-5"/><path d="M5 21h14"/></svg>',
);
return isset( $icons[ $name ] ) ? $icons[ $name ] : '';
}
}
// Bootstrap.
FSC_Free_Section_Code_Viewer::instance();
Everything You Need To Build Better Shopify Stores
Premium quality Shopify sections built with performance, flexibility and developer experience in mind.
Shopify OS 2.0
Fully compatible with all Shopify Online Store 2.0 themes.
Drag & Drop
No coding required. Add sections directly from Theme Editor.
Theme Editor
Every setting is customizable from Shopify Theme Editor.
No App Required
Lightweight code with zero third-party app dependency.
Mobile Responsive
Looks amazing across desktop, tablet and mobile devices.
Speed Optimized
Clean and optimized code built for maximum performance.
SEO Friendly
Semantic HTML and optimized structure for search engines.
Developer Friendly
Readable Liquid, CSS and JavaScript with proper comments.

Reviews
There are no reviews yet.