Wonderful! WordPress

プラグイン Simplistic Prism Highlighter Loader

Page No.1

ちょっと手を入れて使い始めた、prism.js
せっかくなので簡単にロードできるプラグインにでもしてみようかなと思い立ったわけですね。
prism.js 本体のカスタマイズと、classic editor text mode へコードを挿入させるための処理は、自分のものでこのページ → «いまさらのさらば Crayon Syntax Highlighter»に書いたものと同じです。

2023年1月5日、ロードするページの判別部分を主に書き直してバージョンを 3.0 にアップさせたので、その内容でこのページも更新しています。

プラグインとしてつけたかった機能としては、

  • ロードするページを指定できる
  • 他のスタイルシートを選択できる
  • Gutenberg block editor への対応

と、いったところ。
選択できるスタイルシートは、自分がダウンロードしたのは2020年6月の始めだったか、その時ダウンロードすることができた全てのスタイルです。以下のよう。
Funky だけはほぼもとのままで、それ以外は自分の好きなようにカスタマイズしています。好きなようにカスタマイズできるし、オリジナルがよければダウンロードして上書きしてしまえばいいのだけれど、そうすると、独自機能の表示が乱れてしまうと思われるので、ならば、こんなプラグインは使わずに、自分でロードさせたほうが早いと思うのでありますね。

  • Default customized
  • Dark customized
  • Funky (ほぼ元のまま、expand機能だけ)
  • Coy customiezed
  • Okaidia customized
  • Twilight customized
  • Tomorrow Night customized
  • Solarized Light customized
  • STX - Crayon like based Coy

と、いうことでまずはプラグイン本体はこうなってます。
いままで、いくつかの自分のプラグインでなんら問題なく動いてくれていたオプションあたりではあるものの、速さを重視して省いていた部分もあるので、それを速さも考えつつ、不足のないように刷新させています。

<?php
// クラスが定義済みか調べる
if ( ! class_exists( 'smplc_prism_hilighter' ) ) {

class smplc_prism_hilighter {

    private $current;
	private $default;
	private $ret_option;
    private $option_name;
    private $page_prop = [];
    private $pre_tag_flg = '';
    private $pre_tag_ids = [];
    private $showscript;

	public function __construct() {
        $this->option_name = 'smplcprsm_hili_option';

        $this->default = array(//オプションのデフォルト値設定
            'onlyhome' => '',// トップページに限定:top、起動しない:noboot、指定なし、自動判別起動、auto
            'onlysingle' => '',// シングルページに限定:single
            'onlycate' => '',// categoly アーカイブページに限定:all または slugを,区切りリスト
            'onlytag' => '',// tag アーカイブページに限定:all または slugを,区切りリスト
            'onlytax' => '',// カスタムタクソノミー アーカイブページに限定: all または slugを,区切りリスト
			'directpage' => '',// 固定ページに限定(全ての固定ページ):all、特定の固定ページに限定、固定ページ名を入力
            'directcustom' => '',// 特定のカスタム投稿に限定、カスタム投稿名を入力
            'unload' => '',// 指定したページはロードしない
            'editor_popup_parent' => 'wp-content-editor-container',// default eitor画面でpopup-div を挿入する対象となる親ボックス指定
            'hili_font_size' => '',// font-size of highlighter box
            'en_pre_tag' => '',// 記事中に既存する<pre>タグを有効化する、デフォルトは""でdisable、有効にするには正規表現のパターンを両端のデリミタ抜きで登録する
            'replacement_lang' => 'php',// 上行の記事中に既存する<pre>タグを有効化する場合に、language が取得出来なかった場合のデフォルト
            'stylesheet' => ['0'],// select style sheet 0:stx, 1:default, 2:dark, 3:funky, 4:coy, 5:okaidia, 6:twilight, 7:tomorrow night, 8:solarized light
            'en_gutenblock' => '0',// gutenberg block editor でのhighlighter 用のcustom code block の登録、1:enable、0:disable
        );
        
		$this->current = get_option( $this->option_name );

		if ( false === $this->current ) {// 初使用の時などオプションが設定されていない時

			update_option( $this->option_name, $this->default );// デフォルトでオプション設定
			$this->ret_option = $this->default;

        } else {
			// 保存されている値とデフォルトの値で共通するキーだけの配列を取得
			// 保存されている値の配列、デフォルトの値の配列、共通するキーによる配列、それぞれの個数が全て同じならオプション値は新たに追加、または削除されたりしていない
			$deff = array_intersect_key( $this->default, $this->current );
			$countary = array( count ( $this->default ) , count ( $this->current ) , count ( $deff ) );

			if ( 1 !== count ( array_unique( $countary ) ) ) {
				foreach ( $this->current as $key => $val ) {
					if ( isset ( $this->default[ $key ] ) ) {// デフォルトオプションにそのキーが存在する要素だけ保存されている値で上書き。
						$this->default[ $key ] = $val;
					}
				}
				update_option( $this->option_name, $this->default );
				$this->ret_option = $this->default;

			} else {
				$this->ret_option = $this->current;
			}
		}

        // Gutenberg 用 custom code blockをregister する関数を登録
        if ( '1' === $this->ret_option['en_gutenblock'] ) {
            add_action( 'init', array( $this, 'smplcprsm_cgb_register_block' ) );
        }

        // アクセスされたページの情報をwp hook にて取得
        add_action( 'wp', array( $this, 'set_page_prop' ) );

        // 取得したページの情報をヘッダーにて書き出す処理、無くても影響なし
        // add_action('wp_head', array( $this, 'show_page_prop') );

		//↓サイト読み込み時にプラグインのスタイルシートを登録
        add_action( 'wp_enqueue_scripts', array( $this, 'smplcprsm_hili_scrpts' ) );

        // classic editor においてエンティティしたコードをエディタに挿入するためのjavascript をフッターに書き出すための登録
        add_action( 'admin_print_footer_scripts', array( $this, 'appthemes_add_quicktags' ) );

        // コードを広げて表示させるためのdiv 要素を書き出すための処理とプラグイン本体のjsファイルをロードさせるためのjsコードを登録
        add_action( 'wp_footer', array( $this, 'add_expand_div' ) );

        // crayon から乗り換えた場合等、既存のpre タグを有効化するための処理の登録
        if ( $this->ret_option['en_pre_tag'] ) {
            if ( false !== strpos( $this->ret_option['en_pre_tag'], '!' ) ) {
                $tmpary = explode( '!', $this->ret_option['en_pre_tag'] );
                $this->pre_tag_flg = $tmpary[0];
                if ( isset( $tmpary[1] ) ) {
                    $this->pre_tag_ids = explode( ',', $tmpary[1] );
                }
            } else {
                $this->pre_tag_flg = $this->ret_option['en_pre_tag'];
            }

            add_filter( 'the_content', array( $this, 'apply_pre_tag' ) );
        }

        // Disable plugins auto-update UI elements.
        add_filter( 'plugin_auto_update_setting_html', array( $this, 'smplcprmhl_auto_update_setting_html' ), 10, 3 );
    }

    // このプラグインのオートアップデートをフックを使ってdisable にする。
    public function smplcprmhl_auto_update_setting_html( $html, $plugin_file, $plugin_data ) {
        if ( 'simplistic_prism_highlighter_loader/smplc_prism_highlighter.php' === $plugin_file ) {
            $html = __( 'Auto-updates are not available for this plugin.', 'smplc_prism_highlighter' );
        }
     
        return $html;
    }

    // ↓ class 続く
?>
PHP
CopyExpand

ロードさせるページの判別においては、 $wp_query の情報を使っています。それが下の4行目からの関数。その $wp_query の情報は、アクションフック、 wp の時点での取得。
ただし、 wp-includes/class-wp-query.php を見ていると、実際には WordPress はもっと丁寧というか複雑なことをやってページの属性を判断しています。それゆえに複雑な構造のサイトの場合だと、たぶん正確に判別できないところもあるだろうと思います。しかし、自分のローカルサイトとこのサイトで試したところ、一応、もれなく判別できているようなので、あまり処理を複雑にすることよりも、なるべく軽く、より速くということでこれだけで留めました。その点、ご留意を。

そしてv3.0においての最たる変更点は、主たる2つの Javascript ファイル、prism.jssmplcprsm_highlighter_sub.js のロード方法。従来は普通に phpWordPress 標準の方法である wp_enqueue_script でロードさせていたものを、Javascript でロードさせる方法に変更したこと。これは pre タグがあるページだけでロードとか、キャッシュファイルを使っているページではロードさせないとかといった判別を可能にするため。

あと、ロードさせるページの判別方法も、オプションに設定してある項目を全てチェックする方法から、呼ばれているページが何であるかで判別する方法に変更した。この方が、通過する if 文の条件の数が少なくてすむと思う。

<?php
    // ↓ class 続き
    // アクセスされたページの情報を$wp_query から取得してプラグインの起動を判別させる関数を呼び出す関数
    public function set_page_prop() {
        global $wp_query;

        $q_vars = $wp_query->query_vars;

        $this->page_prop['isfront'] = is_front_page();
        if ($q_vars['post_type'] ) {
            $this->page_prop['iscustompost'] = $q_vars['post_type'];
        } else {
            $this->page_prop['iscustompost'] = '';
        }
        $this->page_prop['issingle'] = $wp_query->is_single;
        $this->page_prop['ispage'] = $wp_query->is_page;
        if ( $q_vars['cat'] ) {
            $this->page_prop['iscategory'] = $q_vars['cat'];
        } else {
            $this->page_prop['iscategory'] = '';
        }
        if ( $q_vars['tag_id'] ) {
            $this->page_prop['istag'] = $q_vars['tag_id'];
        } else {
            $this->page_prop['istag'] = '';
        }
        if ( isset( $q_vars['term'] ) ) {
            $this->page_prop['istax'] = $q_vars['term'];
        } else {
            $this->page_prop['istax'] = '';
        }

        $q_post = $wp_query->post;
        if ( isset( $q_post->ID ) ) {
            $this->page_prop['postid'] = ( string ) $q_post->ID;
        } else {
            $this->page_prop['postid'] = '0';
        }
        // 取得したページの情報に基づいてプラグインの起動を判別させる関数を呼び出す
        $this->showscript = $this->smplcprsm_load_direct();
    }

    // ロードさせるcssスタイルシートを指定する関数
    public function specify_style() {
        $member = [ 'stx', 'default', 'dark', 'funky', 'coy', 'okaidia', 'twilight', 'tomorrow_night', 'solarized_light' ];
        $ssheet = $this->ret_option['stylesheet'];

            // 指定されているスタイルシートが一つの場合はそのシートを指定
        if ( 1 === count ( $ssheet ) ) {
            $csssheet = 'prism_' . $member[ ( int ) $ssheet[0] ] . '.css';
        } else {// 複数指定されている場合は、その中からランダムに選んで指定
            $sheetnum = mt_rand( 0, ( count( $ssheet ) - 1 ) );
            $csssheet = 'prism_' . $member[ $ssheet[ $sheetnum ] ] . '.css';
        }
        return $csssheet;
    }

    //サイト読み込み時にcssスタイルシートを登録する関数
	public function smplcprsm_hili_scrpts() {

		// $this->showscript にはロードするページか判別する関数からの結果が入っている
		if ( 1 === $this->showscript ) {

            $csssheet = $this->specify_style();

			wp_enqueue_style( 'prism_style', plugins_url( $csssheet, __FILE__ ), false, date( 'YmdHis', filemtime( plugin_dir_path( __FILE__ ).$csssheet ) ) );
        }
	}

    // コードを広げて表示するための隠しdiv 要素と本体の2つのjavascript ファイルをロードさせるためのjavascript コードの登録
    public function add_expand_div() {

        // $this->showscript にはロードするページか判別する関数からの結果が入っている
        if ( 1 === $this->showscript ) {

			// そのサイトにContent-Security-Policyのnonceが設定されている場合、scriptを稼働させるためにはそのnonceが必要となる
			$cps = headers_list();
			$cpsnonce = '';
			$pattern = '/\'nonce-([0-9a-fA-F].*?)\'/';

			foreach ( $cps as $val ) {
				if ( false !== strpos( $val, 'Content-Security-Policy' ) ) {
					if ( preg_match ( $pattern, $val, $matches ) ) {
						$cpsnonce = ' nonce="' . $matches[1] . '"';
					}
					break;
				}
			}			
?>
            <!-- Simplistic Prism Highlighter loader Javascript output -->
            <div id="hl_tx_expand"></div>

            <script type="text/javascript"<?php echo $cpsnonce; ?>>
                ( function() {
			// jsスクリプトファイルをロードさせるための関数
                    function loadScript( url, id, callback ) {
                        const doc = document,
                            script = doc.createElement( 'script' );

                        script.type = "text/javascript";
                        script.id = id;

                        script.onload = function() {
                            callback();
                        };

                        script.src = url;
                        doc.getElementsByTagName( 'head' )[0].appendChild( script );
                    }

                    const doc = document,
                        pretag = doc.getElementsByTagName( 'pre' ),
                        usecache = doc.getElementById( 'usecache' );

                    // そのページにpreタグが存在しなければロードしない 
                    if ( pretag.length ) {
                        const loadsrc = [];

     			// 自サイトにおいては独自キャッシュを使用していて、ページコンテンツがキャッシュで表示されている場合は、id=usecache の要素が存在し、その場合は prism.js は必要ないのでロードさせない
                        if ( null === usecache ) {
                            loadsrc.push( [ '<?php echo plugins_url( 'prism.js', __FILE__ ); ?>', 'prism_js', '<?php echo date( 'YmdHis', filemtime( plugin_dir_path( __FILE__ ).'prism.js' ) ); ?>' ] );
                        }
                        loadsrc.push( [ '<?php echo plugins_url( 'smplcprsm_highlighter_sub.js', __FILE__ ); ?>', 'smplcprsm_hili_js', '<?php echo date( 'YmdHis', filemtime( plugin_dir_path( __FILE__ ).'smplcprsm_highlighter_sub.js' ) ); ?>' ] );

                        loadScript( loadsrc[0][0] + '?ver=' + loadsrc[0][2], loadsrc[0][1] , function() {
                            console.log( 'ready loaded ' + loadsrc[0][1] + ' file' );
                            if ( loadsrc[1] ) {
                                loadScript( loadsrc[1][0] + '?ver=' + loadsrc[1][2], loadsrc[1][1] , function() {
                                    console.log( 'ready loaded ' + loadsrc[1][1] + ' file' );
                                });
                            }
                        });
                    }
                })();
            </script>
<?php
        }
    }
?>
    // ↓ class 続く
PHP
CopyExpand
<?php
     // ↓ class 続き
     // classic editor text mode にクイックタグを登録し、
     // そのボタンに、popup を表示させエンティティしたコードをエディタに挿入するためのjavascript 関数を登録
   public function appthemes_add_quicktags() {
        if ( wp_script_is( 'quicktags' ) ) {

            $fontsize = '';
            if ( '' !== $this->ret_option['hili_font_size'] ) {
                $fontsize = ' style="font-size:' . str_replace( array( '<', '"' ), '', $this->ret_option['hili_font_size'] ) . ';"';
            }

            $editor_popup_parent = str_replace( array( '<', '"' ), '', $this->ret_option['editor_popup_parent'] );
?>
            <script type="text/javascript">
                window.addEventListener( 'load', () => {
                    QTags.addButton( 'strixss_hl', 'highlighter', sss_highlighter_popup );
        
                    function sss_highlighter_popup() {
                        var doc = document,
                            prcl;
        
                        if ( null == doc.getElementById( 'highlighter' ) ) {
                            var parr = doc.createElement('div');
                            
                            parr.id = 'highlighter';
                            prcl = '<button type="button" id="hili_insert" style="margin-bottom:10px;">insert</button>';
                            prcl += '<button type="button" id="hili_cancel" style="margin-bottom:10px;">cancel</button><br>';
                            prcl += 'name:<input type="text" id="cordtitle" value="PHP" style="width:110px;">';
                            prcl += ' lang:<input type="text" id="clang" value="php" style="width:80px">';
                            prcl += ' mark:<input type="text" id="linemark" value="" style="width:80px">ex.3,6-9';
                            prcl += ' <input type="checkbox" id="randomnumber">:irregular number<br><textarea id ="hili_text"></textarea>Simplistic Prism.js highlighter Loader';
                            parr.innerHTML = prcl;
        
                            parr.style.cssText = 'position:fixed;width:700px;height:700px;top:calc(50vh - 350px);right:calc(50vw - 350px);padding:10px;background:linear-gradient(to right, #CECEA2, white, #CECEA2);text-align:center;color:#0000CC;border-radius:10px;box-shadow:3px 3px 3px 3px rgba(0,0,0,0.5);transition:all 0.1s;';
        
                            parr.style.zIndex = 1001;
        
                            doc.getElementById( '<?php echo $editor_popup_parent; ?>' ).appendChild( parr );
                            doc.getElementById( 'hili_text' ).style.cssText = 'position:relative;width:95%;height:85%;text-align:left;margin:5px auto;';
                            doc.getElementById( 'hili_text' ).focus();

                            doc.getElementById( 'hili_insert' ).onclick = () => {
                                highlighter_entiti();
                            }

                            doc.getElementById( 'hili_cancel' ).onclick = () => {
                                erase_highlighter();
                            }
                        
                        } else {
                            doc.getElementById( 'highlighter' ).style.cssText = 'position:fixed;width:700px;height:700px;top:calc(50vh - 350px);right:calc(50vw - 350px);padding:10px;background:linear-gradient(to right, purple, white, purple);text-align:center;color:white;border-radius:10px;box-shadow:3px 3px 3px 3px rgba(0,0,0,0.5);transition:all 0.1s;';
                            doc.getElementById( 'hili_text' ).focus();
                            doc.getElementById( 'hili_text' ).value = '';
                        }
                    }
        
                    function erase_highlighter() {
                        document.getElementById( 'highlighter' ).style.cssText = 'opacity:0;visibility:hidden;z-index:0;';
                    }
        
                    function highlighter_entiti() {
                        var doc = document,
                            fullcont = doc.getElementById( 'content' ),
                            tartext = doc.getElementById( 'hili_text' ).value,
                            cordtitle = doc.getElementById( 'cordtitle' ).value,
                            clang = doc.getElementById( 'clang' ).value,
                            linemark = doc.getElementById( 'linemark' ).value,
                            rdmnum = doc.getElementById( 'randomnumber').checked,
                            irregular = '',
                            fullcontext = fullcont.value,
                            fullcontext_len = fullcontext.length,
                            curpos = fullcont.selectionStart,
                            fontsize = '<?php echo $fontsize; ?>';
        
                        entitext = tartext.replace( /</g, '<' );
                        if ( linemark ) {
                            linemark = ' data-line="' + linemark + '"';
                        }
        
                        if ( rdmnum ) {
                            irregular = ' data-numbers="5-10,_2,25-35,40"'; 
                        }

                        var before   = fullcontext.substr( 0, curpos ),
                            after    = fullcontext.substr( curpos, fullcontext_len );
        
                        fullcont.value = before + "\n\n" + '<div class="highlighter_js" data-name="' + cordtitle + '"' + irregular + fontsize + '>' + "\n" + '<pre class="line-numbers"' + linemark + '><code class="language-' + clang + '">' + entitext + "\n" + '</code></pre></div>' + "\n\n" + after;
        
                        doc.getElementById( 'highlighter' ).style.cssText = 'opacity:0;visibility:hidden;z-index:0;';
                    }
                });
            </script>
<?php
        }
    }
    // ↓ class 続く
?>
PHP
CopyExpand

Leave a Reply!

JavaScript is necessary to send a comment.
You can edit and delete your comment if you input a edit key.
Edit key is necessary for attesting you when you edit and delete it.
The tag of HTML cannot be used in comment.
When you comment for the first time, it is displayed after the approval of the administrator.
Because I cannot speak English so much, it takes time to answer.
Required fields are marked *.

※Please enter more than 5 characters only alphabets.
※Edit or delete are possible for 2000 days after approval.

*

♠Simplistic Comment User Editable v4.0

♠When visitors leave comments on the site this site collect the data shown in the comments form, and also the visitor’s IP address and browser user agent string to help spam detection.
♠This site does not use cookie when visitors leave comments and commenter edit comment.
♠This site uses Akismet to reduce spam. Learn how your comment data is processed.

Comments feed

Trackback URL : https://strix.main.jp/wp-trackback.php?p=159063

Sanbanse Funabashi
2011.01.01 sunrise

Top

スクロールさせるか画像をクリックすると元に戻ります。