DigiPress

Highly Flexible WordPress Theme

[WP]ページ内に特定のブロックがある時のみ専用の CSS, JavaScript を読み込む方法

カスタマイズ・技術情報

WordPress のブロックエディター(Gutenberg)や FSE (フルサイト編集) 機能の登場によって、様々なコンテンツを記事もサイトも「ブロック」単位で自由に組み立てて構築できるようになりました。

さらにコンテンツの構成単位である「ブロック」は、プラグインやテーマの開発者がオリジナルのものをいくつでも組み込めます。
また、WordPress 標準のコアブロックに対しても開発者は必要に応じて独自の CSS を適用できます。

しかし、対象とする全てのブロックの CSS や JavaScript のファイルを1つにまとめてしまうと、拡張する標準ブロックや提供する独自ブロックが多くなるほど不要なリソースを消費させ、レスポンス低下の原因になってしまいます。

そこで、対象のブロックごとに必要な CSS や JavaScript を細分化し、さらに「ページ内に対象のブロックが存在する場合のみ必要な CSS や JavaScript を読み込ませる」ようにする必要があります。

ブロックテーマの場合、wp_enqueue_block_style という便利な関数が提供されており、この関数でブロックごとに CSS を登録すれば、対象ブロックがレンダリングされるときのみ必要な CSS を head セクション内にインラインで読み込ませることができます。

基本書式
function custom_enqueue_block_style() {
    wp_enqueue_block_style(
        // 対象とするブロック名(スラッグ)
        'core/paragraph',
        array(
            // 登録する CSS のハンドル名(識別ID)
            'handle'	=> 'extended-paragraph',
            // 読み込ませたい CSS ファイルのパス (テーマの場合は get_theme_file_uri )。
            'src'	=> plugins_url( 'css/core-paragraph.css' ),
            // path を指定しておくと、スタイルシートの内容が一定量以下ならばインライン化する?(テーマの場合は get_theme_file_path )
            'path'	=> plugin_dir_path( __FILE__ ) . 'css/core-paragraph.css',
            // 依存関係にある(先に読み込ませておくべき) CSS のハンドル名
            'deps'	=> array()
        )
    );
}
add_filter( 'after_setup_theme', 'custom_enqueue_block_style' );

wp_enqueue_block_style の第2パラメータ(array)に src とは別に path というキーで CSS ファイルのパスを指定しておくと、対象の CSS ファイルが一定のサイズ以下であれば、CSS ファイルの内容をインラインで出力します。
(逆に、スタイルシートのコード量が多すぎる場合はインラインでなく link 要素になる?)

その他のパラメータや詳細について公式ドキュメントを参照してください。

クラシックテーマの場合

テーマがクラシックテーマの場合、プラグイン側で wp_enqueue_block_style を利用してパフォーマンスの向上を図っても、wp_enqueue_block_style で登録された CSS は対象ブロックの存在有無は関係なくすべて読みこんでしまいます。
従来の wp_enqueue_style で読み込む場合の違いは、link 要素でファイルとして読み込まれるか style 要素でインライン化されるかだけの違いになります。

また、クラシックテーマの場合は、WordPress 5.8 にて実装されたshould_load_separate_core_block_assets フィルターフックを利用すればブロックの種類ごとに CSS を読み込ませることが可能になりますが、head セクションではなく、body 要素内に CSS が読み込まれてしまうことと、CSS の読み込み順序が変わってしまうため、今まで上書きしていたスタイルが反映されなくなるなど、色々と影響があります。

add_filter( 'should_load_separate_core_block_assets', '__return_true' );

そこで、今回は別の方法でクラシックテーマも考慮したブロック単位での CSS と JavaScript のキューへの追加方法を考えてみます。


フィルターフックを使い分ける

wp_enqueue_block_style 関数は、通常 after_setup_theme フィルターフックのタイミングで利用されることが多いようですが、ブロックがレンダリングされる直前に処理を挟むことができる render_block フィルターでも利用できます。

ブロックの種類ごとでフックできる render_block_{block_name} をトリガーとすることで、クラシックテーマであっても対象ブロックが実際にレンダリングされるときのみ、専用の CSS や JavaScript を読み込ませることができそうです。

例えば、見出しブロック(core/heading)のレンダリング時に処理をしたい場合は、render_block_core/heading となります。

フィルターフックの使い分け

使用するフック名役割
after_setup_theme・ブロックテーマ用のカスタム CSS の登録
render_block_{block_name}・クラシックテーマ用のカスタム CSS の登録
・特定のブロック用の JavaScript の登録(共通)

ブロックテーマ用のカスタム CSS の登録(キュー追加)

ブロックテーマの場合は、シンプルに wp_enqueue_block_style のみで特定のブロックがレンダリングされるときのみ、インラインスタイルを挿入できるので、ブロックテーマの場合のみ実行させます。

サンプル
function additional_after_setup_theme(){
    // クラシックテーマの場合はスルー
    if ( ! wp_is_block_theme() ) return;

    // 対象のブロック(ここに必要なだけ追加)
    $blocks = [
        'core/heading',	// 見出し
        'core/paragraph',	// 段落
        'core/button'	// ボタン
    ];

    foreach( $blocks as $block ) {
        $args       = array(
            'handle' 	=> 'custom-' . esc_attr( str_replace( '/', '-', $block ) ),
            //  テーマの場合は get_theme_file_uri( css/' . $block . '/style.css' )
            'src'    	=> plugins_url( 'css/' . $block . '/style.css', __FILE__ ),
            //  テーマの場合は get_theme_file_path( css/' . $block . '/style.css' )
            'path'	=> plugin_dir_path( __FILE__ ) . 'css/' . $block . '/style.css'
        );
        wp_enqueue_block_style( $block, $args );
    }
}

add_filter( 'after_setup_theme', 'additional_after_setup_theme' );

このサンプルでは、{plugin_dir}/css/{block_name}/style.css に CSS ファイルがある環境を前提にしています。
例 : https://mydomain.com/wp-content/plugins/my-plugin/css/core/heading/style.css

クラシックテーマ用のカスタム CSS の登録

クラシックテーマの場合は、render_block_{block_name} フックを利用して、クラシックテーマの場合のみ、wp_enqueue_block_style でカスタム CSS をインライン出力させます。

サンプル
// 対象のブロック(ここに必要なだけ追加)
$blocks = [
    'core/heading',
    'core/paragraph',
    'core/button'
];

function additional_render_block( $block_content, $block ){
    // ブロックテーマの場合はスルー
    if ( wp_is_block_theme() ) return;

    $args  = array(
        'handle'	=> 'custom-' . esc_attr( str_replace( '/', '-', $block['blockName'] ) ),
        'src'		=> plugins_url( 'css/' . $block['blockName'] . '/style.css', __FILE__ ),
        'path'		=> plugin_dir_path( __FILE__ ) . 'css/' . $block['blockName'] . '/style.css',
    );

    wp_enqueue_block_style( $block['blockName'], $args );

    return $block_content;
}

foreach( $blocks as $block ) {
    // 3番目のパラメータ{ 2 }は関数に渡す引数の数($block_content, $block)
    add_filter( 'render_block_' . $block, 'additional_render_block', 10, 2 );
}

JavaScript の登録

JavaScript に関しては CSS のwp_enqueue_block_style のような関数はないため、先述の render_block_{block_name} フックを利用した関数内でキューに追加します。

この処理は、ブロックテーマ、クラシックテーマ共に実行されます。

サンプル
// 対象のブロック(ここに必要なだけ追加)
$blocks = [
    'core/heading',
    'core/paragraph',
    'core/button'
];

function additional_render_block( $block_content, $block ){
    // クラシックテーマの場合のみ
    if ( ! wp_is_block_theme() ) {
        $args  = array(
            'handle' 	=> 'custom-' . esc_attr( str_replace( '/', '-', $block['blockName'] ) ),
            'src'    	=> plugins_url( 'css/' . $block['blockName'] . '/style.css', __FILE__ ),
            'path'	=> plugin_dir_path( __FILE__ ) . 'css/' . $block['blockName'] . '/style.css',
        );

        // カスタム CSS の登録
        wp_enqueue_block_style( $block['blockName'], $args );
    }

    // script.js ファイルがあるときのみ
    if ( file_exists( plugin_dir_path( __FILE__ ) . 'js/' . $block['blockName'] . '/script.js' ) ) {
        // JavaScript の登録(キュー追加)
        wp_enqueue_script(
            'custom-' . esc_attr( str_replace( '/', '-', $block['blockName'] ) ),
            plugins_url( 'js/' . $block['blockName'] . '/script.js', __FILE__ ),
            array(),
            false,
            true
        );
    }

    return $block_content;
}

foreach( $blocks as $block ) {
    add_filter( 'render_block_' . $block, 'additional_render_block', 10, 2 );
}

このサンプルでは、{plugin_dir}/js/{block_name}/script.js にファイルがある環境を前提にしています。
例 : https://mydomain.com/wp-content/plugins/my-plugin/js/core/heading/script.js

まとめる(クラス化)

以上の処理をまとめると、クラス化したほうが効率的なのでクラス化しておきます。

class My_Custom_Block_Style {
    // 対象のブロック
    public static $blocks = [
        'core/heading',
        'core/paragraph',
        'core/button'
    ];

    function __construct() {
        add_filter( 'after_setup_theme', array( __CLASS__, 'additional_after_setup_theme' ) );
        foreach( self::$blocks as $block ) {
            add_filter( 'render_block_' . $block, array( __CLASS__, 'additional_render_block' ), 10, 2 );
        }
    }

    public static function additional_after_setup_theme(){
        if ( ! wp_is_block_theme() ) return;

        foreach( self::$blocks as $block ) {
            $args       = array(
                'handle' 	=> 'custom-' . esc_attr( str_replace( '/', '-', $block ) ),
                'src'    	=> plugins_url( 'css/' . $block . '/style.css', __FILE__ ),
                'path'		=> plugin_dir_path( __FILE__ ) . 'css/' . $block . '/style.css'
            );
            wp_enqueue_block_style( $block, $args );
        }
    }

    public static function additional_render_block( $block_content, $block ){
        if ( ! wp_is_block_theme() ) {
            $args  = array(
                'handle' 	=> 'custom-' . esc_attr( str_replace( '/', '-', $block['blockName'] ) ),
                'src'    	=> plugins_url( 'css/' . $block['blockName'] . '/style.css', __FILE__ ),
                'path'		=> plugin_dir_path( __FILE__ ) . 'css/' . $block['blockName'] . '/style.css',
            );

            wp_enqueue_block_style( $block['blockName'], $args );
        }

        if ( file_exists( plugin_dir_path( __FILE__ ) . 'js/' . $block['blockName'] . '/script.js' ) ) {
            wp_enqueue_script(
                'custom-' . esc_attr( str_replace( '/', '-', $block['blockName'] ) ),
                plugins_url( 'js/' . $block['blockName'] . '/script.js', __FILE__ ),
                array(),
                false,
                true
            );
        }

        return $block_content;
    }
}

new My_Custom_Block_Style();

このクラスをプラグイン化したり、テーマの function.php に組み込むことで、ブロックテーマ、クラシックテーマのどちらであっても、ページ内に特定のブロックがある場合のみ独自の CSS を head セクション内にインライン出力し、</body> の前に任意の JavaScript を読み込ませることができます。

Share / Subscribe
Facebook
Posts
Hatena Bookmarks
Pinterest
Pocket
Feedly
Send to LINE
AAAWgmp1bWIAAAAeanVtZGMycGEAEQAQgAAAqgA4m3EDYzJwYQAAABZcanVtYgAAAEdqdW1kYzJtYQARABCAAACqADibcQN1cm46YzJwYTo3MmI1NmY0Mi1mOTkxLTRhZWMtYjI3Zi05NmQ2Y2Q2ZGJmZWIAAAADl2p1bWIAAAApanVtZGMyYXMAEQAQgAAAqgA4m3EDYzJwYS5hc3NlcnRpb25zAAAAALxqdW1iAAAARGp1bWRjYm9yABEAEIAAAKoAOJtxE2MycGEuaW5ncmVkaWVudC52MwAAAAAYYzJzaJpFkg1UlHpZNlVv23V+twUAAABwY2JvcqNpZGM6Zm9ybWF0bWltYWdlL3N2Zyt4bWxqaW5zdGFuY2VJRHgseG1wOmlpZDpiYjI4ZjNiNS02YWYyLTQzZDAtOTdhYi05Y2U4N2E3MzNlMGFscmVsYXRpb25zaGlwaHBhcmVudE9mAAAB4mp1bWIAAABBanVtZGNib3IAEQAQgAAAqgA4m3ETYzJwYS5hY3Rpb25zLnYyAAAAABhjMnNo5gNfqq3PzirY5Ex0DoWaEwAAAZljYm9yomdhY3Rpb25zgqJmYWN0aW9ua2MycGEub3BlbmVkanBhcmFtZXRlcnOha2luZ3JlZGllbnRzgaJjdXJseC1zZWxmI2p1bWJmPWMycGEuYXNzZXJ0aW9ucy9jMnBhLmluZ3JlZGllbnQudjNkaGFzaFggRmYWoSvnUaiaZo0ZsQM96LtvWKoYvEtwD1ZLsm7yrrekZmFjdGlvbngdY29tLmFudGhyb3BpYy5jbGF1ZGUucHJvdmlkZWRqcGFyYW1ldGVyc6F4H2NvbS5hbnRocm9waWMub3JpZ2luLWNvbmZpZGVuY2VndW5rbm93bmtkZXNjcmlwdGlvbnhmQ2xhdWRlIHByb3ZpZGVkIHRoaXMgZmlsZSBhdCB0aGUgcmVxdWVzdCBvZiBhIHVzZXIgYW5kIG1heSBoYXZlIGNyZWF0ZWQgb3IgbW9kaWZpZWQgdGhlIGZpbGUgY29udGVudHMubXNvZnR3YXJlQWdlbnShZG5hbWVmQ2xhdWRlcmFsbEFjdGlvbnNJbmNsdWRlZPUAAADIanVtYgAAAEBqdW1kY2JvcgARABCAAACqADibcRNjMnBhLmhhc2guZGF0YQAAAAAYYzJzaKpVotd9NvpSskj5KUcCIi0AAACAY2JvcqVjYWxnZnNoYTI1NmNwYWRNAAAAAAAAAAAAAAAAAGRoYXNoWCAosBvMqF1+lZe9gmOcHC/J3Y6+rBHLvQJxpFh59mfTKGRuYW1lbmp1bWJmIG1hbmlmZXN0amV4Y2x1c2lvbnOBomVzdGFydBjaZmxlbmd0aBkeBAAAAj5qdW1iAAAAJ2p1bWRjMmNsABEAEIAAAKoAOJtxA2MycGEuY2xhaW0udjIAAAACD2Nib3KlY2FsZ2ZzaGEyNTZpc2lnbmF0dXJleE1zZWxmI2p1bWJmPS9jMnBhL3VybjpjMnBhOjcyYjU2ZjQyLWY5OTEtNGFlYy1iMjdmLTk2ZDZjZDZkYmZlYi9jMnBhLnNpZ25hdHVyZWppbnN0YW5jZUlEeCx4bXA6aWlkOjM2MjQxODYwLTVjZjQtNGFiNy1iMTdkLTgzY2I1NGQ4Mjk1Y3JjcmVhdGVkX2Fzc2VydGlvbnODomN1cmx4LXNlbGYjanVtYmY9YzJwYS5hc3NlcnRpb25zL2MycGEuaW5ncmVkaWVudC52M2RoYXNoWCBGZhahK+dRqJpmjRmxAz3ou29Yqhi8S3APVkuybvKut6JjdXJseCpzZWxmI2p1bWJmPWMycGEuYXNzZXJ0aW9ucy9jMnBhLmFjdGlvbnMudjJkaGFzaFggGA0lysfzsAP33kpMa/AM72oz/8ZsEHu9XyDWwTO5v22iY3VybHgpc2VsZiNqdW1iZj1jMnBhLmFzc2VydGlvbnMvYzJwYS5oYXNoLmRhdGFkaGFzaFggLW0SxOkC51fl8mcYUAJVo/Rm2FBikfsY/PunAD9daDd0Y2xhaW1fZ2VuZXJhdG9yX2luZm+jZG5hbWVvQW50aHJvcGljIEZpbGVzZ3ZlcnNpb25lMS4wLjBrc3BlY1ZlcnNpb25lMi40LjAAABA4anVtYgAAAChqdW1kYzJjcwARABCAAACqADibcQNjMnBhLnNpZ25hdHVyZQAAABAIY2JvctKEWQISogEmGCFZAgowggIGMIIBjaADAgECAhRA5aAK7sI50L64g/oGQgU9Z1UTADAKBggqhkjOPQQDAzBJMRcwFQYDVQQKEw5BbnRocm9waWMsIFBCQzEuMCwGA1UEAxMlQW50aHJvcGljIENvbnRlbnQgQ3JlZGVudGlhbHMgUm9vdCBDQTAeFw0yNjA4MDcxODQzNTZaFw0yODA4MDYxOTQzNTZaMEQxFzAVBgNVBAoTDkFudGhyb3BpYywgUEJDMSkwJwYDVQQDEyBBbnRocm9waWMgQ2xhdWRlIENvbnRlbnQgU2lnbmluZzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJh6CmvLUBgFFNU0vUKlOVtE6djd17L5SuwX0LemFisBM3dkd/3cyjxFA3Qo5S46fX0/ihY0VZ7mfb9KF703t5OjWDBWMA4GA1UdDwEB/wQEAwIHgDAVBgNVHSUEDjAMBgorBgEEAYPoXgIBMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUzlHiBIFOZFsj+OPEz5o+nMHXXMIwCgYIKoZIzj0EAwMDZwAwZAIwMXMdFJ4BetLLVY7ORuE9noqbbAZOZn/aArXyTwFAZfKrPzxF2vPoJNf1+UCdg1XGAjBwX1zd9WGqYkqmL5SFqw1QySjr1zJfpJM9+1rdDwSPLMOPOjKuiXjoU/pUUeG9RwmhY3BhZFkNngAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPZYQOX0HxJBDEy+kPFgsqx5au8boT29bL+Ruh52/5DgQoYrU+ZyLgBowbwVTBBjsXdI/XKzdHkn4W7kxyR1u/Wk548=