「PreBox Tools ⭐」は、コード掲載用の「pre枠」の操作に必要なツールで、一般のユーザーには縁のないものと思います。 しかし、アメーバの編集画面は「CKEditor」をベースにしていて、殆ど同じ編集画面を使うブログは多く、このツールのコードは移植が可能と思われます。 そんなブログで開発されているユーザーにも、このツールのコードが参考になればと思っています。
「\u200B」の対策
前ページで触れましたが、公開コードに特殊文字「\u200B」が混入する事はたまにあり、ツールを利用するユーザーは、コードが全く機能しないので混乱してしまいます。 これは極力避けるべき事ですが、なにぶん「見えない敵」なので、「pre枠」の整形ツール「PreBox Tools ⭐」に、チェックと削除機能を組込む事にしました。 今から考えると、これは遅すぎたくらいです。
「\u200B」を書き込む方法
この機能の動作チェックは、文字「\u200B」のサンプルが必要です。 しかしこの特殊文字は、普通には記入できません。 書き込む方法は幾つかある様ですが、私が良いと思うのは、HTML編集画面に直接文字コードを書き込む方法です。
この「ゼロ幅スペース」の文字コード「​」を、書き込んだところです。
これで何なんと思うのですが、一度「通常表示」に戻り、もう一度「HTML表示」を開くと、あら不思議。 書いた所が「●」の記号に変わっています。
この方法で文章中に書き込めるのですが、HTML編集画面では「●」をコピー&ペーストができます。 通常表示でもコピー&ペーストは可能ですが、「見えない」のでおうよその場所をコピーする形になります。(貼り付けても見えないので、うまくコピペが出来たかが判らない ^^;)
「PreBox Tools ⭐」の操作
新たに「\u200B」のチェック・削除機能を追加しましたが、これは「 」の処理の後に、続けて実行されます。
❶「Ctrl+F7」でツールを起動する。
❷ 処理対象の「pre枠」を「Ctrl+左Click」する。
これで、本来の「 」➔「半角スペース」の整形処理が働きます。
❸ 続いて「pre枠」内の「\u200B」を検索し、あった場合は削除の判断を訪ねます。
▪「\u200B」が無かった場合は、そのまま処理が終わります。
▪「\u200B」のチェック・削除は「pre枠」内だけの処理で、「pre枠」の外の本文は処理の対象になりません。
通常、❷の処理で「OK」を選択して ❸の処理になりますが、その場合は、❷で処理をアンドゥするための「pre枠」のコピーを作っています。 それは「\u200B」削除前のコピーですから、アンドゥで「 」「\n200B」が復活します。 そのため、❸の確認ダイアログは、以下の形になります。
一方、殆どない事ですが、❷の処理で「キャンセル」を選択した場合、アンドゥのための「pre枠」のコピーを作りません。 その場合、次の❸で「\u200B」を削除すると元に戻せないので、❸の確認ダイアログは以下の形になります。
ここの処理は少し迷った所ですが、「\u200B」の削除時に「pre枠」のコピーを作ると、アンドゥ用のコピーが錯綜して、元に戻せると思ったのが戻らないという事になりかねないので、ここは操作の流れをシンプルにしています。 まあ、普通はいつも「OK」を選択して進むのですが。
ただ、「ゼロ幅スペース」が「1個」は有り得ますが、これが複数の場合は、何か異常事態と考え、削除は「キャンセル」してHTMLを確かめるべきでしょう。 そんな事は、まあ起こらないと思いますが。
「PreBox Tools ⭐」の扱い方の詳細
これは、以下のページに纏めています。 また、「PreBox Tools ⭐」のヘルプボタンからも、同じ記事を参照できます。
「PreBox Tools ⭐」を利用するには
このツールは Chrome / Edge / Firefox版の拡張機能「Tampermonkey」上で動作します。 以下に、このツールの導入手順を簡単に説明します。
❶「Tampermonkey」を導入します
◎ 使用しているブラウザに拡張機能「Tampermonkey」を導入する事が必要です。
既に「Tampermonkey」を導入している場合は、この手順 ❶ は不要です。
拡張機能の導入については、以下のページに簡単な説明があるので参照ください。
❷「Tampermonkey」にスクリプトを登録します
◎「Tampermonkey」の「+」マークの「新規スクリプト」タブを開きます。
◎「新規スクリプト」には、最初からテンプレートが記入されています。 これは全て削除して、完全に空白の編集枠に 下のコードをコピー&ペーストします。
〔コピー方法〕 軽量シンプルなツール「PreBox Button 」を使うと
コード枠内を「Ctrl+左Click」➔「Copy code 」を「左Click」
の操作で、掲載コードのコピーが可能になります。
◎ 最後に「ファイル」メニューの「保存」を押すと、ツールが使用可能になります。
〔 PreBox Tools ⭐ 〕 ver. 1.5
// ==UserScript== // @name PreBox Tools ⭐ // @namespace http://tampermonkey.net/ // @version 1.5 // @description 「pre枠」関連総合編集ツール 「Ctrl+F7」 // @author Ameba Blog User // @match https://blog.ameba.jp/ucs/entry/srventry* // @exclude https://blog.ameba.jp/ucs/entry/srventrylist.do* // @grant none // ==/UserScript== let retry=0; let interval=setInterval(wait_target, 100); function wait_target(){ retry++; if(retry>10){ // リトライ制限 10回 1sec clearInterval(interval); } let target=document.getElementById('cke_1_contents'); // 監視 target if(target){ clearInterval(interval); main(); }} function main(){ let mode=0; let box_mode=0; // boxの状態 let box_i; // boxの選択 let editor_iframe; let iframe_doc; let iframe_body; let wysiwyg; // 通常表示の iframe内 html let buffer=[]; // box内データのバックアップ用配列 let target=document.getElementById('cke_1_contents'); // 監視 target let monitor=new MutationObserver(catch_key); monitor.observe(target, {childList: true}); // ショートカット待受け開始 catch_key(); function catch_key(){ editor_iframe=document.querySelector('.cke_wysiwyg_frame'); if(editor_iframe){ iframe_doc=editor_iframe.contentWindow.document; when_back(); right_select(); document.addEventListener('keydown', check_key); // iframe外 iframe_doc.addEventListener('keydown', check_key); // iframe内 function check_key(event){ if(event.ctrlKey && event.keyCode==118){ // ショートカット「Crtl+F7」 event.preventDefault(); event.stopImmediatePropagation(); if(mode==0){ mode=1; sign(); right_select(); box_act(); } else if(mode==1){ mode=0; buffer=[]; // ツールOFF時にバッファークリア 🔵 sign_clear(); tospace_clear(); box_close(); }}}} before_end(); } // catch_key() function when_back(){ // HTML表示から戻った時に処理状態を再現表示 editor_iframe=document.querySelector('.cke_wysiwyg_frame'); if(editor_iframe){ iframe_doc=editor_iframe.contentWindow.document; if(iframe_doc){ if(mode==1){ sign(); box_act(); }}}} // ********** Menu Display and additional function ************** function sign(){ monitor.disconnect(); // 起動時セットアップを MutationObserverに反応させない editor_iframe=document.querySelector('.cke_wysiwyg_frame'); if(editor_iframe){ iframe_doc=editor_iframe.contentWindow.document; let style_sbf= '<style id="style_sbf">'+ '.open_box { outline: 2px solid #009688 !important; height: auto !important; } '+ '.clean_box { outline: 2px solid #03a9f4 !important; }'+ '</style>'; if(!iframe_doc.documentElement.querySelector('#style_sbf')){ iframe_doc.documentElement.insertAdjacentHTML('beforeend', style_sbf); }} let SVG_h= '<svg id="help_pbt" viewBox="0 0 150 150">'+ '<path fill="#fff" d="M66 13C56 15 47 18 39 24C-12 60 18 146 82 137C92 '+ '135 102 131 110 126C162 90 128 4 66 13M68 25C131 17 145 117 81 '+ '125C16 133 3 34 68 25M69 40C61 41 39 58 58 61C66 63 73 47 82 57C84 '+ '60 83 62 81 65C77 70 52 90 76 89C82 89 82 84 86 81C92 76 98 74 100 66'+ 'C105 48 84 37 69 40M70 94C58 99 66 118 78 112C90 107 82 89 70 94z">'+ '</path></svg>'; let disp_sf= '<div id="disp_sf">'+ SVG_h+' '+ '■ To Space (nbsp): Ctrl+L-Click ■ Code Copy: Ctrl+R-Click ■ Scroll Fix: Alt+Click'+ '</div>'+ '<style>'+ '#cke_1_contents { display: flex; flex-direction: column; } '+ '#disp_sf { position: relative; margin: 0 0 5px; padding: 4px 0 1px; '+ 'font: 16px/24px Meiryo; color: #fff; background: #009688; white-space: nowrap; } '+ '#help_pbt { position: absolute; top: 7px; left: 6px; height: 16px; width: 16px; '+ 'cursor: pointer; }'+ '</style>'; editor_iframe=document.querySelector('.cke_wysiwyg_frame'); if(editor_iframe){ if(!document.querySelector('#disp_sf')){ editor_iframe.insertAdjacentHTML('beforebegin', disp_sf); }} monitor.observe(target, {childList: true}); document.querySelector('#disp_sf').style.display='block'; let help=document.querySelector('#help_pbt'); help.onclick=function(){ window.open("https://ameblo.jp/personwritep/entry-12760312656.html", '_blank'); } } // sign() function sign_clear(){ if(target.querySelector('#disp_sf')){ target.querySelector('#disp_sf').style.display='none'; }} // 起動表示を非表示 // *********** Pre Box Right Select ******************** function right_select(){ editor_iframe=document.querySelector('.cke_wysiwyg_frame'); if(editor_iframe){ iframe_doc=editor_iframe.contentWindow.document; if(iframe_doc){ let iframe_body=iframe_doc.querySelector('body'); if(iframe_body){ let target_pre=iframe_body.querySelectorAll('pre'); for(let i=0; i<target_pre.length; i++){ target_pre[i].oncontextmenu=function(event){ if(event.ctrlKey){ event.preventDefault(); event.stopImmediatePropagation(); select_code(target_pre[i]); }}} function select_code(box){ let selection=iframe_doc.getSelection(); let range=iframe_doc.createRange(); range.selectNodeContents(box); selection.removeAllRanges(); selection.addRange(range); } }}}} // right_select() // ********** Scroll Fix & To Space (nbsp) ************** function box_act(){ let pre_box; // PRE枠のコンテナブロック editor_iframe=document.querySelector('.cke_wysiwyg_frame'); if(editor_iframe){ iframe_doc=editor_iframe.contentWindow.document; if(iframe_doc){ iframe_body=iframe_doc.querySelector('body'); if(iframe_body){ pre_box=iframe_body.querySelectorAll('div'); for(let i=0; i<pre_box.length; i++){ pre_box[i].onclick=function(event){ if(event.altKey){ event.preventDefault(); event.stopImmediatePropagation(); scroll_fix(pre_box[i], i); } if(event.ctrlKey){ event.preventDefault(); event.stopImmediatePropagation(); edit_box(pre_box[i], i); }}} function scroll_fix(box, index){ if(box_mode==0 && mode==1){ box_mode=1; // boxのScroll設定を外す let style_set=window.getComputedStyle(box, null); if(style_set.getPropertyValue('overflow')){ if(style_set.getPropertyValue('overflow')=='scroll' || style_set.getPropertyValue('overflow')=='auto' || style_set.getPropertyValue('overflow')=='overlay'){ box_i=index; box.classList.add('open_box'); }} else if(style_set.getPropertyValue('overflow-y')){ if(style_set.getPropertyValue('overflow-y')=='scroll' || style_set.getPropertyValue('overflow-y')=='auto' || style_set.getPropertyValue('overflow-y')=='overlay'){ box_i=index; box.classList.add('open_box'); }}} else{ if(index==box_i){ box_mode=0; // boxのScroll設定を元に戻す box.classList.remove('open_box'); }} } // scroll_fix() function edit_box(box, index){ let nbsp; let a_count; if(box.firstChild.tagName=='PRE' && mode==1){ if(!box.classList.contains('clean_box')){ let regex=new RegExp('\u00A0', 'g'); let nbsp=box.innerText.match(regex); let pa_count=box.getElementsByTagName("a"); if(nbsp !=null || pa_count.length !=0){ let ok; if(nbsp==null && pa_count.length !=0){ ok=confirm(" ⏬ 「 」 0 個\n"+ " 「a要素」 " + pa_count.length + " 個をテキストに置換えます"); } else if(nbsp !=null && pa_count.length==0){ ok=confirm(" ⏬ 「 」 " + nbsp.length + " 個を半角空白に変換します\n"+ " 「a要素」 0 個"); } else{ ok=confirm(" ⏬ 「 」 " + nbsp.length + " 個を半角空白に変換します\n"+ " 「a要素」 " + pa_count.length + " 個をテキストに置換えます"); } if(ok){ if(buffer[index]==null){ buffer[index]=box.firstChild.innerHTML; } // 選択pre枠のデータバックアップ 🔵 box.classList.add('clean_box'); to_space(box, index); }} else{ box.classList.add('clean_box'); } let regex_z=new RegExp('\u200B', 'g'); let zwsp=box.innerText.match(regex_z); if(zwsp !=null && buffer[index]==null){ let ok=confirm(" 🛑 「\\u200B」(ゼロ幅スペース)" + zwsp.length + "個 が混入しています\n"+ " これを全て削除しますが、削除した「\\u200B」は元に戻せません"); if(ok){ zw_space(); }} else if(zwsp !=null && buffer[index]!=null){ let ok=confirm(" 🛑 「\\u200B」(ゼロ幅スペース)" + zwsp.length + "個 が混入しています\n"+ " これを全て削除します"); if(ok){ zw_space(); }} } // contains('clean_box') else{ let ok=confirm(" ❎ 変換前に戻しますか?"); if(ok){ if(buffer[index]!=null){ box.firstChild.innerHTML=buffer[index]; buffer[index]=null; } // バッファークリア 🔵 box.classList.remove('clean_box'); }}} function zw_space(){ let pre_text=box.firstChild.textContent; pre_text=pre_text.replace(/\u200B/g,''); box.firstChild.textContent=pre_text; } function to_space(box, index){ a_count=0; let child_node=box.firstChild.childNodes; for(let t=0; t<child_node.length; t++){ if(child_node[t].nodeType==3){ child_node[t].nodeValue=child_node[t].nodeValue.replace(/\u00A0/g , '\u0020'); } else if(child_node[t].nodeType==1 && child_node[t].tagName=="A"){ let inner_node=child_node[t].childNodes; if(inner_node.length==1 && inner_node[0].nodeType==3){ box.firstChild.replaceChild(inner_node[0], child_node[t]); } // text を a要素と入替え else{ a_count +=1; }} // 子ノードが1個の textの条件に当てはまらない a要素 else{ let child_node2=child_node[t].childNodes; if(child_node2){ for(let t2=0; t2<child_node2.length; t2++){ if(child_node2[t2].nodeType==3){ child_node2[t2].nodeValue= child_node2[t2].nodeValue.replace(/\u00A0/g , '\u0020'); } else{ let child_node3=child_node2[t2].childNodes; if(child_node3){ for(let t3=0; t3<child_node3.length; t3++){ if(child_node3[t3].nodeType==3){ child_node3[t3].nodeValue= child_node3[t3].nodeValue.replace(/\u00A0/g , '\u0020'); } }}}}}}} let regex=new RegExp('\u00A0', 'g'); nbsp=box.innerText.match(regex); if(nbsp !=null){ alert("❌ 変換できなかった「 」の数 : " + nbsp.length );} if(a_count !=0){ alert("❌ 置換えが出来ない「a要素」 : " + a_count); } } // to_space() } //edit_box() }}}} // box_act() // ********** Scroll Fix additional function ************** function box_close(){ editor_iframe=document.querySelector('.cke_wysiwyg_frame'); if(editor_iframe){ iframe_doc=editor_iframe.contentWindow.document; if(iframe_doc){ box_mode=0; let open_box=iframe_body.querySelectorAll('.open_box'); for(let i=0; i<open_box.length; i++){ open_box[i].classList.remove('open_box'); }}}} // 処理枠の outline を削除 // ********** To Space (nbsp) additional function ************** function tospace_clear(){ editor_iframe=document.querySelector('.cke_wysiwyg_frame'); if(editor_iframe){ iframe_doc=editor_iframe.contentWindow.document; if(iframe_doc){ let clear_box=iframe_doc.querySelectorAll('.clean_box'); for(let i=0; i<clear_box.length; i++){ clear_box[i].classList.remove('clean_box'); }}}} // 処理枠の outline を削除 // ********** Before End ********************************* function before_end(){ editor_iframe=document.querySelector('.cke_wysiwyg_frame'); let submitButton=document.querySelectorAll('.js-submitButton'); submitButton[0].addEventListener("mousedown", all_clear, false); submitButton[1].addEventListener("mousedown", all_clear, false); function all_clear(){ if(!editor_iframe){ //「HTML表示」編集画面の場合 alert("⛔ PreBox Tools ⭐ の終了処理ができません\n\n"+ " 通常表示画面に戻り 編集を終了してください"); event.stopImmediatePropagation(); event.preventDefault(); } if(editor_iframe){ //「通常表示」編集画面の場合 if(mode!=0){ mode=0; box_mode=0; tospace_clear(); box_close(); }} }} // before_end() } // main()
編集画面終了時の処理コードを修正しました。 これに伴い、上記コードはバージョン数を更新しています。
〔 PreBox Tools ⭐ 〕 ver. 1.4 ➔ ver. 1.5
「PreBox Tools ⭐」最新版について
旧いバージョンの JavaScriptツールは、アメーバのページ構成の変更で動作しない場合があり、導入する場合は最新バージョンをお勧めします。
●「PreBox Tools ⭐」の最新バージョンへのリンクは、以下のページのリンクリストから探せます。