# booktics/1.0.28/core/assets/base-assets.php

Booktics – Appointment Booking Calendar for Service Businesses, version 1.0.28. 415 lines.

- Page: https://pluginprobe.com/plugins/booktics/1.0.28/code/core/assets/base-assets.php
- Raw: https://pluginprobe.com/plugins/booktics/1.0.28/raw/core/assets/base-assets.php
- Modified: 2026-10-05T09:02:00+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/booktics/1.0.28/code/core/assets/base-assets.php#L10-L20`.

```php
<?php
namespace Booktics\Assets;

if ( ! defined( 'ABSPATH' ) ) exit;

use Booktics\Contracts\Hookable_Service_Contract;

/**
 * Main assets management class
 */
abstract class Base_Assets implements Hookable_Service_Contract {
    /**
     * Get all scripts
     *
     * @return  array list of script to register
     */
    abstract public function get_scripts(): array;

    /**
     * Get all styles
     *
     * @return  array   list of styles to regiser
     */
    abstract public function get_styles(): array;

    /**
     * Register scripts and styles
     *
     * @return  void
     */
    public function register_styles_scripts() {
        $this->register_global_scripts();
        $this->register_scripts();
        $this->register_styles();
    }

    /**
     * Register scripts
     *
     * @return void
     */
    public function register_scripts() {
        $scripts = $this->get_scripts();

        foreach ( $scripts as $handle => $script ) {
            $deps      = isset( $script['deps'] ) ? $script['deps'] : [];
            $in_footer = isset( $script['in_footer'] ) ? $script['in_footer'] : true;
            $version   = isset( $script['version'] ) ? $script['version'] : $this->get_version( $script['src'] );

            $deps = $this->get_dependencies( $script['src'], $deps );

            if ( in_array( 'wp-i18n', $deps, true ) ) {
                $deps[] = 'booktics-i18n-loader';
            }

            wp_register_script( $handle, $script['src'], $deps, $version, $in_footer );

            if ( function_exists( 'wp_set_script_translations' ) ) {
                wp_set_script_translations( $handle, 'booktics' );
            }

            if ( 'booktics-packages' === $handle ) {
                wp_add_inline_script(
                    'booktics-packages',
                    '(function(){try{if(window.bookticsPreloadedLocaleData && window.wp && window.wp.i18n && window.wp.i18n.setLocaleData){window.wp.i18n.setLocaleData(window.bookticsPreloadedLocaleData,"booktics");}}catch(e){}})();',
                    'before'
                );
            }
        }
    }

    /**
     * Register global scripts.
     *
     * @return void
     */
    private function register_global_scripts() {
        $scripts = [
            'booktics-i18n-loader' => [
                'src'       => booktics()->assets_url . '/build/js/i18n-loader.js',
                'deps'      => [],
                'in_footer' => true,
            ],
        ];

        foreach ( $scripts as $handle => $script ) {
            $deps      = isset( $script['deps'] ) ? $script['deps'] : [];
            $in_footer = isset( $script['in_footer'] ) ? $script['in_footer'] : true;
            $version   = isset( $script['version'] ) ? $script['version'] : $this->get_version( $script['src'] );

            $deps = $this->get_dependencies( $script['src'], $deps );

            wp_register_script( $handle, $script['src'], $deps, $version, $in_footer );
        }
    }

    /**
     * Enqueue i18n loader and set its state.
     *
     * @return void
     */
    protected function enqueue_i18n_loader() {
        $data = [
            'baseUrl'        => false,
            'locale'         => determine_locale(),
            'domainMap'      => [],
            'domainPaths'    => [],
            'translationMap' => (object) [],
            'preloaded'      => false,
        ];

        $lang_dir    = WP_LANG_DIR;
        $content_dir = WP_CONTENT_DIR;
        $abspath     = ABSPATH;

        if ( strpos( $lang_dir, $content_dir ) === 0 ) {
            $data['baseUrl'] = content_url(
                substr( trailingslashit( $lang_dir ), strlen( trailingslashit( $content_dir ) ) )
            );
        } elseif ( strpos( $lang_dir, $abspath ) === 0 ) {
            $data['baseUrl'] = site_url(
                substr( trailingslashit( $lang_dir ), strlen( untrailingslashit( $abspath ) ) )
            );
        }

        $locale_data        = $this->get_preloaded_locale_data( $data['locale'] );
        $data['preloaded']  = ! empty( $locale_data );

        // The consolidated locale data already covers every string in the bundles,
        // so the per-chunk hash map is only needed when it could not be built.
        if ( ! $data['preloaded'] ) {
            $data['translationMap'] = (object) $this->get_translation_map();
        }

        wp_enqueue_script( 'booktics-i18n-loader' );

        $data['domainMap']   = (object) $data['domainMap'];
        $data['domainPaths'] = (object) $data['domainPaths'];

        $inline = '(function(){var k="bookticsI18nLoader"; if (typeof window[k] === "undefined") { window[k] = {}; } window[k].state = ' . wp_json_encode( $data, JSON_UNESCAPED_SLASHES ) . ';';

        if ( ! empty( $locale_data ) ) {
            $inline .= ' window[k].preloadedLocaleData = window.bookticsPreloadedLocaleData = '
                . wp_json_encode( $locale_data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ) . ';';
        }

        $inline .= '})();';

        wp_add_inline_script(
            'booktics-i18n-loader',
            $inline,
            'before'
        );
    }

    /**
     * Register styles
     *
     * @return void
     */
    public function register_styles() {
        $styles = $this->get_styles();

        foreach ( $styles as $handle => $style ) {
            $deps    = isset( $style['deps'] ) ? $style['deps'] : false;
            $version = booktics()->version;

            wp_register_style( $handle, $style['src'], $deps, $version );
        }
    }

    /**
     * Get script and style file dependencies
     *
     * @param   string  $file_name
     * @param   array  $deps
     *
     * @return  array
     */
    private function get_dependencies( $file_name, $deps = [] ) {
        $assets = $this->get_file_assets( $file_name );

        $assets_deps = ! empty( $assets['dependencies'] ) ? $assets['dependencies'] : [];

        $merged_deps = array_merge( $assets_deps, $deps );

        return $merged_deps;
    }

    /**
     * Get file assets
     *
     * @param   string  $file_name
     *
     * @return  array
     */
    private function get_file_assets( $file_url ) {
        $file   = $this->get_file_path( $file_url );
        $assets = [];

        if ( file_exists( $file ) ) {
            $assets = include $file;
        }

        return $assets;
    }

    /**
     * Get script file version from .asset.php (generated by DependencyExtractionWebpackPlugin).
     *
     * @param   string  $file_name
     *
     * @return  string
     */
    private function get_version( $file_name ) {
        $assets      = $this->get_file_assets( $file_name );
        $assets_vers = ! empty( $assets['version'] ) ? $assets['version'] : booktics()->version;
        return $assets_vers;
    }

    /**
     * Get file path from url
     *
     * @param   string  $url
     *
     * @return string|false
     */
    private function get_file_path( $url ) {
        if ( ! filter_var( $url, FILTER_VALIDATE_URL ) ) {
            return false;
        }

        $url_parts = wp_parse_url( $url );

        if ( ! isset( $url_parts['path'] ) ) {
            return false;
        }

        $path = $url_parts['path'];
        $path = preg_replace( '/\.js$/', '.asset.php', $path );

        if ( isset( $_SERVER['DOCUMENT_ROOT'] ) ) {
            $file_path = sanitize_text_field( wp_unslash( $_SERVER['DOCUMENT_ROOT'] ) ) . $path;
        } else {
            $file_path = '';
        }

        if ( ! file_exists( $file_path ) ) {
            return false;
        }

        return $file_path;
    }

    /**
     * Build the Jed locale data for every translated string in the text domain.
     *
     * Script translations are normally split into one JSON file per bundled
     * script, keyed by an md5 of that script's path. Async chunk filenames carry
     * a content hash and change on every build, so those JSON files go stale as
     * soon as the plugin is updated. Registering the whole domain up front keeps
     * the React apps translated regardless of how the bundles were chunked.
     *
     * @param string $locale Locale being rendered.
     *
     * @return array Jed formatted locale data, empty when there is nothing to preload.
     */
    private function get_preloaded_locale_data( $locale ) {
        static $cache = [];

        if ( isset( $cache[ $locale ] ) ) {
            return $cache[ $locale ];
        }

        $cache[ $locale ] = [];

        if ( 'en_US' === $locale ) {
            return $cache[ $locale ];
        }

        if ( ! is_textdomain_loaded( 'booktics' ) ) {
            // Registers the plugin's own /languages path. Just-in-time loading only
            // finds WP.org language packs, so without this a bundled .mo is ignored.
            // phpcs:ignore PluginCheck.CodeAnalysis.DiscouragedFunctions.load_plugin_textdomainFound
            load_plugin_textdomain( 'booktics', false, dirname( plugin_basename( BOOKTICS_FILE ) ) . '/languages' );
        }

        $translations = get_translations_for_domain( 'booktics' );

        // WP_Translations (WP 6.5+, .l10n.php files) does not extend Translations,
        // so check for the entries it exposes rather than the class.
        if ( ! is_object( $translations ) || $translations instanceof \NOOP_Translations ) {
            return $cache[ $locale ];
        }

        $entries  = (array) $translations->entries;
        $messages = [];

        foreach ( $entries as $entry ) {
            if ( ! $entry instanceof \Translation_Entry || ! is_string( $entry->singular ) || '' === $entry->singular ) {
                continue;
            }

            $entry_translations = array_values( array_filter(
                (array) $entry->translations,
                function ( $translation ) {
                    return is_string( $translation ) && '' !== $translation;
                }
            ) );

            if ( empty( $entry_translations ) ) {
                continue;
            }

            // wp.i18n keys contextual strings as "contextsingular".
            $key = ( is_string( $entry->context ) && '' !== $entry->context )
                ? $entry->context . "\4" . $entry->singular
                : $entry->singular;

            $messages[ $key ] = $entry_translations;
        }

        if ( empty( $messages ) ) {
            return $cache[ $locale ];
        }

        $headers = (array) $translations->headers;

        $locale_data = [
            '' => [
                'domain'       => 'booktics',
                'lang'         => $locale,
                'plural-forms' => ! empty( $headers['Plural-Forms'] ) ? $headers['Plural-Forms'] : 'nplurals=2; plural=n != 1;',
            ],
        ] + $messages;

        /**
         * Filter the locale data preloaded for the React apps.
         *
         * Useful to trim the payload down to the strings a site actually renders.
         *
         * @param array  $locale_data Jed formatted locale data.
         * @param string $locale      Locale being rendered.
         */
        $cache[ $locale ] = apply_filters( 'booktics_preloaded_locale_data', $locale_data, $locale );

        return $cache[ $locale ];
    }

    /**
     * Get map of translation file hashes to their URLs.
     *
     * @return array Hash => URL map.
     */
    private function get_translation_map() {
        $map    = [];
        $locale = determine_locale();
        $domain = 'booktics';

        $locale_variants = array_unique( array_filter( [
            $locale,
            str_replace( '-', '_', $locale ),
            str_replace( '_', '-', $locale ),
        ] ) );

        foreach ( $this->get_translation_directories() as $dir ) {
            if ( ! is_dir( $dir['path'] ) || ! is_readable( $dir['path'] ) ) {
                continue;
            }

            foreach ( $locale_variants as $variant ) {
                $files = glob( $dir['path'] . $domain . '-' . $variant . '-*.json' );
                if ( ! $files ) {
                    continue;
                }

                foreach ( $files as $file ) {
                    $filename       = basename( $file );
                    $variant_prefix = $domain . '-' . $variant . '-';
                    if ( strpos( $filename, $variant_prefix ) === 0 ) {
                        $hash = substr( $filename, strlen( $variant_prefix ), -5 );
                        if ( $hash ) {
                            $map[ $hash ] = trailingslashit( $dir['url'] ) . $filename;
                        }
                    }
                }
            }
        }

        return $map;
    }

    /**
     * Return translation directory paths and their corresponding URLs.
     *
     * @return array<int, array{path: string, url: string}>
     */
    private function get_translation_directories() {
        return [
            [
                'path' => WP_CONTENT_DIR . '/languages/plugins/',
                'url'  => content_url( 'languages/plugins/' ),
            ],
            [
                'path' => WP_CONTENT_DIR . '/languages/loco/plugins/',
                'url'  => content_url( 'languages/loco/plugins/' ),
            ],
            [
                'path' => booktics()->plugin_directory . '/languages/',
                'url'  => booktics()->plugin_url . '/languages/',
            ],
        ];
    }
}

```
