なんだか「いまさらの~」がシリーズものになってきたような。
まぁ、それはいいとして、いよいよと言うか、いまさらのさようならCrayon Syntax Highlighter ということで。
相変わらずいらぬことばかりを書き連ねてしまうので、前置きがとても長いのです。
このページのコンテンツは以下のようになってますので、情け容赦なく飛び越していってくださいまし。
phpが7になり、その後のバージョンアップでもエラーやら非推奨が出るようになってしまった。たしか7.3の時のエラーは世界の何処かにいる何方かが修正してくれたもので、とりあえずその場をしのぐことができた。そして今回、7.4でのDeprecated は、implode の引数の並び順によるもの。それはDeprecated なので動かなくなるというものではないけれど。いままでimplode は、それが要求する2つの引数の並び順は固定されていなかった。が、次代ではexplode と同様に並び順が指定されるとのことで、その警告のようなもの。その程度のものであれば、自分で対応でき、無事に問題なく機能してくれていた。
でも、聞いたところでは、すでに開発は何年も前にやめてしまっているとのことであるし、いずれ近いうちに使えなくなるのは目に見えている。ならばそろそろ変え時なんだろうと。世の中ではとっくの前にそういう流れになっていたようで、自分の場合は相変わらずののんびり具合で、やはりいまさらそんなことを言ってんの?という感じなのであろう。
ネットを見ているとCrayon は重い、という情報を良く目にする。確かにロードするファイルの数は多いけれど、それでも自分的にはあまり重いという印象はもっていなかったし、なによりその表示やら使い勝手が気に入っていたんだね。
と、いうところで、その代わりになるものということになる。いっそのこと自作できないものだろうかと、それに関する情報をネットで漁っていたらhighlight.js の存在を知ることになり、とりあえずは試しに使ってみることにした。
« highlighter.js とはいかに »
このhighlight.js 、プラグインではないが導入はいたって簡単で、コンテンツ中のタグ、<pre>に<code>を付加して<pre><code>にし、あとはjsファイルとcssスタイルシートをページにロードさせるだけ。あっさりと機能してくれた。なかなか良い感じではあるけれど、当然のこと、デフォルトであるから十分というにはほど遠い。jsファイルの中身を見ていると、いじれそうである。豊富にあったスタイルの中からとりあえずベースにするものを選択し、そのcssファイルも修正しながら色々といじっていると、かなりいい感じになり、これでやってみようという気になった。親ボックスの<div>を付加して、それを使用して行番号、行による背景色の濃淡、マーキングする行の色指定などをjavascript で付け加えると、なかなかどうして、表示だけはCrayon と同等かそれ以上という感じになってきた。残るは投稿に挿入するときにエンティティする機能(とはいっても、変換するのは<と>だけ)と、それを必要なタグで挟んで、エディタのカーソルのある部分に貼り付ける機能をWordPress の編集画面に装備させること。それもそんなに難しいことではなく、以前にやったクイックタグのボタンにその機能を備え付けることで実装させた。ただひとつ、ちょっと気になったのは、自分が使用しているhighlight.js のバージョンは10.0.3 であったが、IE11ではエラーがでてしまい、動かなかった。その時のエラーはアロー関数だったが、他の箇所でも引っかかっていたようである。
そして少し気にしながらも、いざ本サイトへ実装させようとしていたまさにその時、念の為にとhighlight.js の著作権を調べていたら、それよりももっと軽くてコピー機能もそなえているというprism.js なるものに出会ってしまった。
« それでは prism.js は! »
もっと軽いものがあると知ってしまうと、それはもう知らぬ顔はできないというのが人情というもので。先にちゃんとしらべなさい!ってことだね~、われながらていたらくで、ほんとに。
これは試してみないわけにはいかない。さっそくダウンロード。ファイルのサイズが小さいというのを強調してる。こちらのスタイルは選択制で一つだけ。必要な言語も選ぶ。こちらの導入もhighlight.js 同様でとても簡単。とは言っているが、じつのところ、jsファイルをロードするhtml の文が雑にコピーしてきたもので、書式が間違っていたことになかなか気が付かず、ロードできなくてしばしとまどってしまった。あほらしい。それさえなかったら、いともあっけなく動いていただろうと思うのに。
こちらのprism.js もなかなか良い感じではあるが、やっぱりデフォルトだともう一つという、highlight.js とほぼ同じような印象。こちらもjsファイルを覗いているといじくりまわせそう。タグ付けできるword の設定を増やしつつ、cssともども設定していくと、こちらも表示だけはCrayon とほぼ同等かそれ以上というところまでにすることができた。highlight.js とprism.js 、両方とものjsファイルをいじった感じでは、word の追加など、自分的にはprism.js のほうが付き合いやすいように感じた。それにprism.js の方はダウンロードするときに、それ用の機能のプラグインにチェックを入れておけば、行番号も行の背景色の濃淡、行のマーキング、あとクリップボードへのコピー機能も備えてくれている。そしてこちらはIE11 でも問題なく動いてくれた。
こうなるとおのずとどちらを選択するかは決まってくるというものだろう。
・・・
« prism.js customize & start »
と、いうことでここからやっとprism.js の導入、本編へ突入ということになるのである。
あ~、前置きが長すぎっ!って。
prism.js の導入は、さきにも書いたけれどいたって簡単。
Prism でhighlight 表示させる部分のコードを<pre class="line-numbers" data-line="3,5-6"><code class="language-php">と</code></pre>で囲む。
この時、各タグにおけるオプションの指定は。
<pre> -> class="line-numbers" : 行番号表示
<pre> -> data-line="3,5-6" : マーキング行指定
<pre> -> data-start="-5" : 行番号 スタート数指定
<code> -> class="language-php" : language-言語 でプログラム言語を指定
そしてcssスタイルシートファイルとjsファイルを通常のhtml のやり方でロードさせるだけ。ただそれだけ。ファイルサイズも小さいし、たしかに軽そうではある。
prism.js においてword を追加したのは、今の所javascript とpython だけ。php はkeyword の追加は今の所必要なさそうだけれど、一箇所だけカスタマイズしましたね。
と、いうことでjavascript は以下の部分。内包させる言語によって、prism.js のファイルの内容は違っているのだと思う。自分の場合だと、955行目にjavascript に関しての以下の部分がある。
一番最後のマーキングしてある977行目が付け加えた文。
本当はそれぞれのワードの持つ役割で、タグ付を分けるべきなのだろうけれど、とりあえずひとまとめにして、'jsspecial' でスタイル指定できるようにしてある。
ちなみにワードを挟んでいる'\b'は単語の区切りにマッチするもの。これによって対象のワードが他の単語とつながっていたらマッチしない。要は'.length 'にはマッチするけれど、'alength.'にはマッチしない。
python も同様。自分のファイルだと2349行めにある。まだ少なめ。これからおいおいと。
まぁ、わざわざ新規にタグを設定しなくても、既存の'keyword'とかに追加するのも良いのでは。
そして、php はというと、使っているエディタ、vscode のスタイルでvariable の先頭の$ が赤字になっているので、そこがそうなっていないとどうにも物足りない。なんとか変えられないものかと、いじれそうなところを探していって、結果下のようにすることに。正統的な方法ではないのかもしれないけれど。そして、ちょっと負荷が増えてしまうのかもしれないのだけれど・・・。
これは、自分のprism.js だと、392行目からの、Token.stringify = function stringify(o, language) { の最後の部分にある。カスタマイズしたのは436-440のハイライトしてある部分。本来は439行目の一文だけしかない。
ちなみに条件が増えたので432-433のように配列とincludes を使用した書き方をしたら、ie11 に、「オブジェクトは \'includes\' プロパティまたはメソッドをサポートしていません。」と言われてしまった。フンッ!はやくいなくなれ~っ!
« WordPress 投稿編集画面へのカスタマイズ »
と、いうところで、お次はPrism から離れてWordPress の管理画面、投稿編集ページのカスタマイズ。
コードを投稿に挿入するときに'<'と'>'をエンティティにし、必要なタグで挟んで、エディタのカーソルのある部分に貼り付ける関数を、クイックタグにボタンを追加して、そのボタンに登録する。
・・・後日、Gutenberg でも同じ機能をつけようと思ってじたばた紆余曲折してしまったのだけれど、core/code block をいじくり回していて気がついたことは、この場合にGutenberg は'<' の方しかエンティティにしていないということ。なるほど、言われてみればそれだけでタグとは判断されないはず。と、いうことなれば自分も'>'の方は、ほったらかしということに。
んで、戻って、ちなみに自分の場合、その必要なタグというのは、'<div class="highlighter_js" data-name=""><pre class="line-numbers"><code class="language-php">' という具合。
始めはクイックタグじゃなくて「メディアを追加」ボタンの横にボタンをつけようとしていたのだけれど、まぁ、どちらにしても簡単なこと。とりあえず、一応、「メディアを追加」ボタンの横にもう一つボタンを追加するのは以下のようにアクションフック 'media_buttons' でできる。
で、結局、メディアボタンではなくクイックタグを使うことにしたのだけれど、クイックタグの登録もアクションフックを使って簡単にできる。
こちらの場合、クイックタグを登録する関数はjavascript であり、php で直にフッターにjavascript をエコーさせるので、それならばついでに必要な機能のjavascript の関数もここでエコーさせてしまえばよろし、という塩梅なのである。
自サイトの場合、クイックタグは色々と追加登録してる。実はもっとある。ここに表示させているのは一部だけ。
これはもちろんfunctions.phpにて。
WordPress が 6.0 にバージョンアップしたときに、QTags が undefined であるというエラーがでるようになってしまった。が、これは単純にスクリプトが読み込まれる順番が変わってしまったことによるもの。ゆえに、その時にオンロードイベントの処理を加えてある。それにて一件落着!
<?php
function appthemes_add_quicktags() {
if ( wp_script_is( 'quicktags' ) ) {
?>
<script type="text/javascript">
window.addEventListener( 'load', () => {
QTags.addButton('paragraph','p','<p>','</p>','');
QTags.addButton('hr','hr','<hr>\n','','h');
QTags.addButton('bold','bold','<span style=\"font-weight:bold;\">','</span>','w');
QTags.addButton('through','l-through','<span style=\"text-decoration:line-through;\">','</span>','e');
QTags.addButton('blue','blue','<span class=\"bluecolor\">','</span>','n');
QTags.addButton('red','red','<span class=\"redcolor\">','</span>','r');
QTags.addButton('green','green','<span class=\"greencolor\">','</span>','g');
QTags.addButton('cop','cop','<span class=\"copcolor\">','</span>','y');
QTags.addButton('br','br','<br>','','');
QTags.addButton('lg','lg','[lg l="j" s="n"]','[/lg]','l');
QTags.addButton('magiri','magiri wing','<span class=\"magiri\">・・・</span>','','');
QTags.addButton('ls','limit single','[ls]','[/ls]','');
QTags.addButton( 'strix_hl', 'highlighter', highlighter_popup );
function 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" onclick="highlighter_entiti()" style="margin-bottom:10px;">insert</button>';
prcl += '<button type="button" id="hili_cancel" onclick="erase_highlighter()" 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>';
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, 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;';
parr.style.zIndex = 99;
doc.getElementById( 'wp-content-editor-container' ).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();
} 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;
var 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 + '>' + "\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
}
}
add_action( 'admin_print_footer_scripts', 'appthemes_add_quicktags' );
?>
CopyExpand‹›‹›
20行目で今回本命の関数をひもづけたボタンを登録してる。
この場合、注意が必要なのはコールバック関数を指定する場合、その関数名はクォートで囲んではいけない、ということ。ついついうっかりやってしまうけれど。そうするとただの表示する文字列としてしか扱ってくれない。
64行目はエディタの本文における現在のカーソル位置の取得。その位置の前後で本文を分割し、挿入したい成形されたコードをそれに挟み込ませて、再びエディタのウィンドウに貼り付けるということをやってるわけです。
本文のコードを入力するボックス以外に入力欄は3つ。
ファイルの名前や関数名を表示するための表題、タグに設定すべき言語名、マーキングのラインナンバー、と入力したものをタグに設定してエディタへと挿入します。あともう一つチェックボックスがあるのは、独自機能である不規則な行番号を表示するためのもの。
« 独自機能の追加のjavascript »
と、いうことでその独自機能について。
一つはその不規則に設定した行番号の表示機能。
もう一つは大きなウィンドウを表示してコードを表示する機能。見やすくなるかな~?
そしてもう一つ、Crayon から思っていたことだけれど、縦に長いコードボックスで横にもボックスからはみ出してしまう長いコードになった場合、スマホならいいんだけれど、PC の場合だと横スクロールさせるのがちょっと面倒。そのボックスをクリックしてアクティブ状態にすればキーボードの← → キーでもスクロールさせられるのだけれどね・・・。どうせなら、ということで、そういう場合にクリックしてスクロールできるように← → を出してスクロールさせられるようにした。そして、かなり長いボックスの場合を考えて、一画面に一セットは入っているように複数セットすることにしたと。そのボックスにオンマウスで表示される。
不規則行番号は<pre><code> の親ボックスとしてつけている <div> に' data-numbers="5-10,_2,25-35,40"'という具合に設定することで機能します。アンダーバーはその付属する数字のぶんだけ空白の行を作るという意味。この機能はあまり使わないかもしれないのだけれど。たとえばcss ファイルの内容を、その行番号をつけてとびとびに表示する場合なんかに使えるかと。例としてすぐ下ので不規則な行番号を表示させてみる。指定はこれで、
"5-10,_2,25-35,_5,100-146,200-221,_3,96-97,1000-1060"
以下はその部分のjavascript 。この部分はページに通常ロードさせてるjsファイルに加えたもの。
なんだけれども、実はこれ、はじめは jQuery にて書いたのだけれど、後になって少しでもページ表示を高速化するために、jQuery ファイルの読み込み自体を無くそうと改めてプレーンな javascript に書き直してる。それはこちらにて→ 「プラグイン Simplistic Prism Highlighter Loader」
jQuery( function() {
var open_flg = 0;
if ( jQuery( '.highlighter_js' ).length ) {
jQuery( '.highlighter_js' ).each( function( i, o ) {
const mark = jQuery( o ).data( 'numbers' ),
parentHeight = parseInt( jQuery( o ).innerHeight() ),
tartag = jQuery( o ).find( 'code' ),
codescrlwidth = tartag.get(0).scrollWidth,
winheibasis = jQuery( window ).height() * 0.8;
let parr_width = parseInt( jQuery( o ).css( 'width' ) ),
arwflg = '';
if ( codescrlwidth > parr_width ) {
let posrasio = Math.floor( parentHeight / winheibasis ),
toppos = 100 / ( posrasio + 1 ),
eachtoppos;
if ( posrasio < 1 ) {
toppos = 50;
posrasio = 1;
}
for ( let i = 0; i < posrasio; i++ ) {
eachtoppos = toppos * ( i + 1 );
arwflg += '<span class="scrlarw lscrlarw" style="top:' + eachtoppos + '%;">‹</span><span class="scrlarw rscrlarw" style="top:' + eachtoppos + '%;">›</span>';
}
}
if ( 'undefined' !== typeof mark ) {
parr_width -= 40;
jQuery( o ).css( { width: parr_width + 'px' } );
const tarcont = tartag.text();
let lines,
lines_len = 0,
tmpmark = [],
mark_len = 0;
lines = tarcont.split( '\n' );
lines_len = lines.length;
tartag.prepend( '<div id="hl_back' + i + '" class="hl_back"></div>' );
let tarson = jQuery( '#hl_back' + i );
if ( -1 === mark.indexOf( ',' ) ) {
tmpmark[0] = mark;
} else {
tmpmark = mark.split( ',' );
}
mark_len = tmpmark.length;
for ( let k = 0; k < mark_len; ++k ) {
if ( -1 !== tmpmark[ k ].indexOf( '-' ) ) {
let tmpval = tmpmark[ k ].split( '-' );
for ( let h = parseInt( tmpval[0] ); h <= parseInt( tmpval[1] ); ++h ) {
tarson.append( '<span class="hl_line">' + h + '</span>' );
}
} else if ( -1 !== tmpmark[ k ].indexOf( '_' ) ) {
let tmpval = parseInt( tmpmark[ k ].replace( '_', '' ) );
for ( let h = 0; h < tmpval; ++h ) {
tarson.append( '<span class="hl_line"> </span>' );
}
} else {
tarson.append( '<span class="hl_line">' + tmpmark[ k ] + '</span>' );
}
}
}
jQuery( o ).append( '<span class="hl_expand" data-idnum="' + i + '">Expand</span>' );
} );
if ( 0 === open_flg ) {
let wscrn = jQuery( '<div>' );
wscrn.attr( 'id', 'hl_tx_expand' );
wscrn.css( { display: 'none', position: 'fixed', width: '100%', height: '100%', top:'0px', left: '0px', padding: '10px 30px', textAlign: 'center', background: 'rgba(0,0,0,0.5)', zIndex: '0' } );
jQuery( '#mainframe').append( wscrn );
jQuery( wscrn ).append( '<textarea id="hl_expand_tx"></textarea><p id="hl_dell_expand">x</p>' );
jQuery( '#hl_expand_tx' ).css( { width: '0vw', height: '0vh', margin: '10px auto', padding: '20px', lineHeight: '1.4em', background: 'white', opacity: '0',transition: 'width 0.7s, height 1.0s' } );
jQuery( '#hl_dell_expand' ).css( { position: 'absolute', width: '28px', height: '28px', top: '30px', right: '100px', padding: '10px', borderRadius: '5px', background: 'white', boxShadow: '2px 2px 2px 2px #cccccc' } );
open_flg = 2;
}
jQuery( '.hl_expand' ).click( function() {
const parr = jQuery( this ).parent(),
bro_code = parr.find( 'code' ),
bro_cont = bro_code.text(),
bro_id = parseInt( jQuery( this ).data( 'idnum' ) );
if ( 2 === open_flg ) {
jQuery( '#hl_expand_tx').text( bro_cont );
jQuery( '#hl_tx_expand' ).css( { display: 'block', zIndex: 99 } );
setTimeout( function() {
jQuery( '#hl_expand_tx' ).css( { width: '90vw', height: '90vh', opacity: '1' } ).focus();
}, 500 );
open_flg = 1;
}
});
jQuery( '#hl_dell_expand' ).click( function() {
jQuery( '#hl_expand_tx' ).css( { width: '0vw', height: '0vh' } );
setTimeout( function() {
jQuery( '#hl_expand_tx' ).css( { opacity: '0' } );
jQuery( '#hl_tx_expand' ).css( { display: 'none', zIndex: 0 } );
}, 500 );
open_flg = 2;
} );
jQuery( '.rscrlarw' ).click( function() {
const parr = jQuery( this ).parent(),
bro_code = parr.find( 'code' );
sideScroll( bro_code, 1 );
});
jQuery( '.lscrlarw' ).click( function() {
const parr = jQuery( this ).parent(),
bro_code = parr.find( 'code' );
sideScroll( bro_code, -1 );
});
function sideScroll( bro_code, ope ){
const codescrlwidth = bro_code.get(0).scrollWidth,
moveval = 30;
let currentPos = bro_code.scrollLeft(),
intID = null,
tmpv = 0;
if ( currentPos < codescrlwidth || currentPos > 0 ) {
intID = setInterval( function() {
if ( currentPos > codescrlwidth || currentPos < 0 ) {
clearInterval( intID );
}
currentPos += tmpv * ope;
bro_code.scrollLeft( currentPos );
tmpv++;
if ( tmpv > moveval ) {
clearInterval( intID );
intID = null;
}
}, 30 );
}
}
}
});
5678910 2526272829303132333435 100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146200201202203204205206207208209210211212213214215216217218219220 969710001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061
CopyExpand‹›‹›‹›‹›
Syntax Highlighter の機能的には以上でおわり。
« 既存のpreタグの有効化 »
ただ、自サイト的には忘れてはいけないことがもう一つあって。
それは既存のページにあるCrayon 表示用に<pre> タグだけがついているコード群。それらをページが出力されるときに変換させないとhighlighter 表示にはなってくれない。基本的に、こんな具合のタグがついてる、
'<pre class="lang:js mark:11 decode:true " title="JavaScript" >'
これは正規表現を使うしかありませんね。
コンテンツを出力するときのフィルターフック、'the_content'を使います。
もともとの言語設定を抜き出して使っていますが、もとが'default' となっているとちょっとやっかいです。それをそのままは使えない。かといって内容から判断させるとかは処理が重くなりそうなので、あっさりはしょってます。とりあえずはもっとも多そうな'php' にしてごまかしてますね。
と、いうところで'Syntax Highlighter' の入れ替えがほぼおわりました。
軽くなったか?と聞かれれば、実感的にはあまり変わりないというかんじ。それぞれのword にタグ付けして分別すること自体、処理の負荷が大きいのではないだろうか。前のCrayon はサーバーのphp によるもので、対するprism.js はローカルマシンにおけるJavaScript ということで、どちらに軍配が?ということだと思うのだけれど。
軽くなった実感はほとんどないのだけれど、それでも、バージョンアップのたびに警戒する必要はもうなくなったし、なにより、プラグインだったCrayon は中身が良くわからなかったけれど、その点、prism.js はグッと身近になったという感じがうれしいかも。わが手中にある、という感じがね。
Comments feed
Trackback URL : https://strix.main.jp/wp-trackback.php?p=158389