ブログのメンテナンスでは連続処理をする場合が多い
ブログで「誤字」の修正を行う場合、先ず「ブログ内検索」で「誤字」を全記事で検索します。「誤字」を発見した場合、記事をひとつずつ再編集画面に開きます。
全記事の検索では、「Ameba Search Repeat 」から、記事の検索リストから直接編集が出来る様になり、処理がスムーズになりました。
再編集画面を開いた後、「S-R in Editor ⭐」で再び記事内を「誤字」検索して「置換」の修正をしますが、これは実際の文面チェックが不可欠です。 記事のチェックなしに全記事で「検索/置換」をするツール「Every Page Rewiter」等も制作していますが、これは定型記入の要素等の修正に適し、一般の誤字修正に使うのは無謀です。
しかし、多数の「誤字修正」を行うと、再編集画面を開く毎に同じ「検索文字」と「置換文字」の入力操作を繰り返す事になります。 当然、「検索文字」をコピーして検索文字枠にペーストしますが、「置換文字」は選択式クリップボード等を使わないとコピペできません。
何時間もこの様な事を繰り返していると、「S-R in Editor ⭐」に「検索文字」「置換文字」を覚えさせれば済むと思い始めました。 検索ツールを起動すれば、直接に「置換チェック」の状態になるコードを考えましたが、これは少し難攻しました。
ローカルストレージを使う
JavaScriptのツールでデータ記録と読出しを行うには、ローカルストレージを使うのが便利です。 今回の記録データは僅かなメモ程度で済みます。
上はストレージの読込みコードです。 ストレージから読出したデータを「sr_data」という配列に入れて、スクリプト内で使います。 この配列は4個の簡素なものです。
sr_data[0]:連続処理モード 「0: OFF」「1: ON」
sr_data[1]:一括/選択の処理区別 「1: 一括処理」「2: 選択処理」
sr_data[2]:値は「検索文字列」
sr_data[3]:値は「置換文字列」
「置換チェック画面」の段階で sr_data[1] ~ sr_data[3] が揃うので、それらをストレージに記録します。 これは、検索/置換の要点を記録するという事になります。
連続処理モードのコードの配置
「連続処理モード」のコードは、ツールの主要コードの先頭部に置いています。 ツールの主要コードが動作を始めた最初で、ストレージを読み、sr_data[0] から連続処理モード「ON」として動作するかどうかを決めます。
▶ 連続処理モードがOFFなら、先頭部コードを読み飛ばし、従来通りに主要コードの検索ツールから動作を開始します。
▶ 連続処理モードがONなら、先頭部の「連続処理モード」のコードを実行します。
ここで、「連続処理モード」のコードは、後方の従来の主要コードを部分的に利用していて、本来なら行われる「検索文字」「置換文字」の入力と確定の作業を飛ばして、直接に「置換チェック」の段階を実行します。
連続処理モードのコード
「S-R in Editor ⭐」は、段階を踏んで「置換チェック」に至る、少し複雑な構成ですから、各段階で辿る幾つかのパラメーターや処理結果を適切に代入しないと、段階を飛ばしたコードは上手く動作しません。 問題になるパラメーターは以下でした。
t_flag==0 // TEXT処理未決定 または HTML処理
t_flag==1 // TEXT処理 TEXT一括置換
t_flag==2 // TEXT処理 TEXT選択置換
p_flag==0 // 検索文字 未確定
p_flag==1 // 検索文字確定 または 入力
p_flag==2 // 置換文字確定 または 入力
p_flag==3 // 置換チェック
count_t>0 // 検索によるテキストのヒットカウント有り
コード全体を何度か見直し、「連続処理モード」には「HTML処理」は対象外とし、「TEXT処理」のみを扱うのが無難と判断しました。「HTML処理」を複数の文書で連続して行う場面は殆ど無いと思われ、処理分岐を整理し易くするため「TEXT処理」のみに絞ったのです。
先頭部の「連続処理モード」のコードは以下です。
このコードが動作して、「S-R in Editor ⭐」の起動時に「検索文字」「置換文字」がセットされた「置換チェック画面」がいきなり表示されます。
「連続処理モード」は、間の操作を飛ばす事が目的ですが、この「置換チェック」は「ESC」「UNDO」等で抜ける事が出来ます。 その場合は、後方の本来の主要コードに動作が移ります。 その本来の動作は、「連続処理モード」の「検索文字」「置換文字」を変更する場合に必要になります。
「検索文字」「置換文字」「一括/選択」の登録
「連続処理モード」で使用する「検索文字」「置換文字」「一括/選択」の内容は、「置換チェック画面」で登録します。 そのため「連続処理モード」のスイッチは、チェック画面上だけに表示される様にしています。(HTML処理では表示されません)
上は、「連続処理モード」が「ON」になった状態です。 次に別の編集画面を開いて「S-R in Editor ⭐」を起動すると、登録した「検索/置換」を実行しようとします。 勝手に置換えが実行されるのではありませんが、「連続処理モード」の処理をしない時は、ボタンをクリックして「OFF」にしておきます。 OFFの状態はストレージに登録され、次にONにするまでは従来の「S-R in Editor ⭐」の動作になります。
このボタンで「連続処理モード」を「ON」にする時に、同時にその時の「検索文字」「置換文字」「一括/選択」をストレージに登録し、以降の連続処理はそれを使って行われます。
登録するには、何かの記事で「置換チェック画面」まで操作を進める必要がありますから、検索語のヒットが無い記事で登録が出来ません。 開いている記事にヒットが無い場合は、わざと検索語を記入してから登録する必要があります。
また、「連続処理モード」を「ON」にする時に常に登録が伴うと扱い難いので、前回の連続処理の開始を選択できる様にしています。
「OFF」状態のボタンを押すと下のダイアログが表示されます。「OK」なら現在の状態を登録して「連続処理モード」がONとなり、「キャンセル」を押すと以前の登録内容で「連続処理モード」がONになります。
「連続処理モード」実装の評価
「連続処理モード」のコードは、最初は動作せず実装を諦めかけ、その後に動作にこぎつけたものの、やっつけで少しきわどいコードという気がしています。「連続処理モード」で「S-R in Editor ⭐」をON/OFFすると、「置換チェック画面」の黒背景が表示されない事があり、1secと2sec後に表示指示を繰り返す苦肉の策をしています。
本当は「連続処理」に特化した別ツールに纏めるのが良い気がします。「HTML処理」はコード全体から対象外とし、「置換チェック画面」に至るプロセスで省けるものがあるかも知れません。
その意味で、「連続処理モード」を実装したバージョンは「S-R in Editor ⭐ C」として、本来のツールと区別しました。「C」タイプは「連続処理」に特化を試みるもので、今回はテストバージョンです。
「S-R in Editor ⭐ C」ver. 3.0 を使用するには
このツールは Chrome / Edge / Firefox の拡張機能「Tampermonkey」上で動作します。
❶「Tampermonkey」の導入
以下のページを参照ください。
❷「Tampermonkey」にスクリプトを登録
●「Tampermonkey」の「+」マークの「新規スクリプト」タブを開きます。
●「新規スクリプト」には、最初からテンプレートが記入されています。 これは全て削除して、完全に空白の編集枠に 下のコードをコピー&ペーストします。
〔コピー方法〕 軽量シンプルなツール「PreBox Button 」を使うと
コード枠内を「Ctrl+左Click」➔「Copy code 」を「左Click」
の操作で、掲載コードのコピーが可能になります。
● 最後に「ファイル」メニューの「保存」を押すと、ツールが使用可能になります。
〔 S-R in Editor ⭐ C 〕 ver. 3.0
// ==UserScript==
// @name S-R in Editor ⭐ C
// @namespace http://tampermonkey.net/
// @version 3.0
// @description 通常編集枠で実行できる 検索 / 置換 ツール
// @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 p_flag; // Process
let t_flag; // TEXT処理
let t_or_h=1; // TEXT・HTML選択スイッチ用フラグ
let arg_t_or_h; // TEXT・HTML選択が必要な場合
let count_t;
let count_h;
let buffer;
let buffer_arr=[];
let avoid=[];
let caution;
let hk; // ハイライト要素のインデックス
let native_hk; // フォーカス要素のインデックス履歴
let editor_iframe;
let iframe_doc;
let iframe_html;
let iframe_body;
let js_cover;
let search_box;
let search_word;
let search_word_es;
let replace_box;
let replace_word;
let result_box;
let s_1;
let s_2;
let s_3;
let s_4;
let s_5;
let s_6;
let s_7;
let s_8;
let sr_data=[]; // 連続処理モードの検索/置換データの登録
reg_set();
function reg_set(){
let read_json=localStorage.getItem('sr_editor'); // ローカルストレージ 保存名
sr_data=JSON.parse(read_json);
if(sr_data==null){
sr_data=['0', '1', '', '']; }} // 連続処理モード「0」連続処理OFF「1」は一括処理
let cke_1_contents=document.getElementById('cke_1_contents'); // 監視 target
let monitor=new MutationObserver(catch_key);
monitor.observe(cke_1_contents, {childList: true}); // ショートカット待受け開始
catch_key();
function catch_key(){
if(document.querySelector('.l-gHeaderLeft__link a')){ // 起動を「トップページ」アイコンに表示 📛
document.querySelector('.l-gHeaderLeft__link a').style.boxShadow='inset -14px 0 0 0 #79fbf6'; }
search_box=document.querySelector('#search_box');
editor_iframe=document.querySelector('.cke_wysiwyg_frame');
if(editor_iframe){ //「通常表示」の場合
if(search_box){
add_mu_style(); // muタグ用 styleをiframeに再設定
search_box.disabled=false; }
document.addEventListener("keydown", check_key); // documentは先に指定
iframe_doc=editor_iframe.contentWindow.document;
if(iframe_doc){
iframe_doc.addEventListener("keydown", check_key); } // iframeは後に指定
function check_key(event){
if(event.ctrlKey==true){
if(event.keyCode==123){
if(p_flag!=3){ // 置換チェック時でなければ「Ctrl+F12」でON/OFF
event.stopImmediatePropagation();
search_replace();
if(!editor_iframe){
if_html(); }}}
else if(event.keyCode==13){
if(p_flag==3){
event.stopImmediatePropagation();
pusher(); }}}
if(event.keyCode==27){
if(p_flag==3){ // 置換チェック時に「Esc」で置換チェックを解除=UNDO
event.preventDefault();
out_p_flag3(); }}
if(event.keyCode==9){
if(p_flag==0){ // 編集時に編集枠から「Tab」で検索に戻る
event.preventDefault();
search_box.focus(); }}
if(event.keyCode==9 || event.keyCode==16 || event.keyCode==17 ||
event.keyCode==18 || event.keyCode==19 || event.keyCode==32){
if(p_flag==3){ // 置換チェック時に「Tab/Shift/Ctrl/Alt/Pause/Space」を無効化
event.preventDefault(); }}}}
else{ //「HTML表示」の場合
if_html(); }
function out_p_flag3(){ // 置換チェックを解除する
iframe_body.innerHTML=buffer; // 置換処理をUNDO ⏹
get_search();
js_cover_remove();
s_8.style.display='none';
reset_mu_style();
if(t_flag>0){ //「buffer」を戻したので再度ハイライト表示
t_flag=1;
t_process(); // 🔳 RegExp
next(hk); } // UNDO時の巡回表示
replace_box.focus();
p_flag=2; } // 2=置換入力
function if_html(){ //「HTML表示」を開いた場合のクローズ処理
let s_container=document.querySelector('#s_container');
if(s_container){
search_box.disabled=true;
result_box.textContent=' ';
s_1.style.display='none';
replace_box.style.display='none';
replace_box.value='';
s_2.style.display='none';
s_3.style.display='none';
s_4.style.display='none';
s_5.style.display='none';
s_8.style.display='none';
t_flag=0;
p_flag=0; }}
function pusher(){
s_2.click(); }
} // catch_key
function disp_s_container(){
monitor.disconnect(); // MutationObserverを 起動表示に反応させない
let l_body=document.querySelector('body.l-body');
let insert_node_d=document.createElement('div');
insert_node_d.setAttribute('id', 's_container');
l_body.appendChild(insert_node_d);
insert_node_d.innerHTML=
'<input id="search_box" placeholder=" 検索文字" autocomplete="off">'+
'<span id="result"> </span>'+
'<span class="s_sw s_6">▲ Caution: '+
'<span class="c_nb">nbsp</span> <span class="c_lt"><</span> '+
'<span class="c_gt">></span> <span class="c_am">&</span></span>'+
'<span class="s_sw s_1"> </span>'+
'<input id="replace_box" placeholder=" 置換文字" autocomplete="off">'+
'<span class="s_sw s_2">OK</span>'+
'<span class="s_sw s_3">UNDO</span>'+
'<span class="s_sw s_4"></span>'+
'<span class="s_sw s_5"></span>'+
'<span class="s_sw s_7">✖ 閉じる</span>'+
'<span class="s_sw s_8">C</span>';
let css=
'#s_container {position: absolute; top: 12px; left: calc(50% - 490px); '+
'background: #fff; border: 1px solid #aaa; border-radius: 4px; '+
'padding: 6px 15px; min-width: 948px; z-index: 11;}'+
'#search_box {width: 210px;} #replace_box {width: 210px; display: none;}'+
'::placeholder {font-size: 15px; color: #bbb;}'+
'#s_container input:disabled {color: #000; background: #eef1f3;}'+
'#s_container input {font-size: 16px; padding: 2px 6px 0; -moz-appearance: none;}'+
'#result {display: inline-block; min-width: 50px; padding: 4px 6px 2px; '+
'margin-left: 5px; border: 1px solid #aaa; font-size: 16px;}'+
'.s_sw {display: inline-block; vertical-align: -9px; font-size: 15px; '+
'padding: 5px 8px 2px; border: 1px solid #aaa; overflow: hidden;}'+
'.s_1 {margin: 0 15px; min-width: 4em; display: none;} .s_1 span {color: #fff; }'+
'.s_2, .s_3, .s_4 {color:#fff; background: #1e88e5; cursor: pointer; display: none;}'+
'.s_3, .s_5 {margin-left: 5px;} .s_2, .s_4 {margin-left: 15px;}'+
'.s_5 {position: fixed; top: 70px; right: calc(50% - 490px); width: 165px; '+
'padding: 10px 10px 8px 20px; border-radius: 4px; background: #e3f2fd; display: none;}'+
'.s_5 span {display: inline-block; height: 21px; margin: 5px 3px; padding: 0 3px; '+
'outline: 1px solid #aaa; line-height: 24px; background: #fff;}'+
'.s_6 {margin-left: -1px; background: #ffcc00; display: none; white-space: nowrap;}'+
'.s_6:hover {width: auto !important; padding: 5px 8px 2px !important;}'+
'.c_nb, .c_lt, .c_gt, .c_am {font-weight: bold;}'+
'.s_7 {position: absolute; right: 15px;}'+
'.s_8 {margin-left: 20px; color: #fff; background: #ddd; cursor: pointer; display: none;}'+
'.js_cover {position: fixed; top: 0; width: 100%; height: 100%; '+
'background: rgba(0, 0, 0, .6); z-index: 10; display: none;}';
let style_tag=document.createElement("style"); // css設定styleタグ
style_tag.setAttribute("class", "bp");
style_tag.appendChild(iframe_doc.createTextNode(css));
if(!l_body.querySelector('.bp')){
l_body.appendChild(style_tag); }
add_mu_style(); // muタグを設定
monitor.observe(cke_1_contents, {childList: true});
} // disp_s_container() 「開始処理」
function s_container_remove(){
let s_container=document.querySelector('#s_container');
monitor.disconnect(); // MutationObserverを 起動表示に反応させない
delete_mu();
native_hk=-1; // 初期化🅿
disp_js_cover();
js_cover.style.display='block';
js_cover.style.background='transparent';
js_cover.style.height='60px';
s_container.remove();
setTimeout(()=>{
js_cover.style.display='none';
js_cover.style.background='rgba(0, 0, 0, .6)';
js_cover.style.height='100%'; }, 2000);
monitor.observe(cke_1_contents, {childList: true});
} // s_container_remove() 「終了処理」
function disp_js_cover(){
monitor.disconnect(); // MutationObserverを 起動表示に反応させない
js_cover=document.createElement("div"); // クリック操作のブロックカバー
js_cover.setAttribute("class", "js_cover");
if(!document.querySelector('.js_cover')){
document.querySelector('#js-container').appendChild(js_cover); }
js_cover=document.querySelector('.js_cover');
monitor.observe(cke_1_contents, {childList: true}); }
function js_cover_remove(){
js_cover.style.display='none';
cke_1_contents.style.zIndex='3';
iframe_body.contentEditable='true'; // 編集可能にする
search_box.disabled=false;
replace_box.disabled=false;
s_2.style.display='none';
s_3.style.display='none';
s_4.style.display='none';
s_5.style.display='none';
s_7.style.display='inline-block'; }
function search_replace(){
editor_iframe=document.querySelector('.cke_wysiwyg_frame');
if(editor_iframe){ //「通常表示」の場合
iframe_doc=editor_iframe.contentWindow.document;
iframe_html=iframe_doc.querySelector('html');
iframe_body=iframe_doc.querySelector('body.cke_editable'); }
disp_js_cover();
let s_container=document.querySelector('#s_container');
if(s_container){ // #s_container がある場合は「Ctrl+F12」で終了 「muタグを削除」
s_container_remove(); }
else if(!s_container){ //#s_containerが無い場合 生成して開始
disp_s_container();
search_box=document.querySelector('#search_box');
result_box=document.querySelector('#result');
replace_box=document.querySelector('#replace_box');
s_1=document.querySelector('.s_1');
s_2=document.querySelector('.s_2');
s_3=document.querySelector('.s_3');
s_4=document.querySelector('.s_4');
s_5=document.querySelector('.s_5');
s_6=document.querySelector('.s_6');
s_7=document.querySelector('.s_7');
s_8=document.querySelector('.s_8');
if(sr_data[0]==1){ // 連続処理の場合
search_word=sr_data[2];
search_box.value=sr_data[2]; // 🟥 検索文字取得
get_search(); // 🔳 RegExp
result_box_disp();
if(count_t==0){
alert(" ✅ 連続検索に設定された検索文字がありません"); }
if(count_t>0){
setTimeout(()=>{
replace_process(); }, 20);
setTimeout(()=>{
replace_word=sr_data[3]; // 🟥 置換文字取得
replace_box.style.display='inline-block';
replace_box.value=sr_data[3];
cke_1_contents.style.zIndex='11';
iframe_body.contentEditable='false'; // 編集不可にする
search_box.disabled=true;
replace_box.disabled=true;
s_2.style.display='inline-block';
s_3.style.display='inline-block';
s_4.textContent='一括 選択';
s_4.style.display='inline-block';
if(sr_data[1]==1){
t_flag=1;
s_4.style.boxShadow='inset -45px 0 0 0 #b0bec5'; }
else if(sr_data[1]==2){
t_flag=2;
s_4.style.boxShadow='inset 45px 0 0 0 #b0bec5'; }
s_5.style.display='inline-block';
disp_help();
s_7.style.display='none';
s_8.style.background='#333';
s_8.style.display='inline-block';
js_cover.style.display='block';
add2_mu_style();
p_flag=3;
if(sr_data[1]==1){ // 一括置換
t2_process(); } // 🔳 RegExp
else if(sr_data[1]==2){ // 選択置換
iframe_body.innerHTML=buffer; // 置換処理を一旦デフォルトに戻す ⏹
get_search();
t_process(); } // 🔳 RegExp
native_hk=-1;
hk=0;
next(hk); }, 30);
setTimeout(()=>{
js_cover.style.display='block'; }, 1000);
setTimeout(()=>{
js_cover.style.display='block'; }, 2000);
function disp_help(){
if(t_flag==1){
s_5.innerHTML='<span>⇦</span><span>⇧</span><span>⇩</span>'+
'<span>⇨</span>:移動<br><span>OK</span> / <span>Ctrl</span>'+
'+<span>Enter</span><br> :置換を確定する<br>'+
'<span>UNDO</span> / <span>Esc</span><br> :全て元に戻す'; }
else if(t_flag==2){
s_5.innerHTML='<span>⇦</span><span>⇧</span><span>⇩</span>'+
'<span>⇨</span>:移動<br><span>Space</span>:設定 / 解除<br>'+
'<span>OK</span> / <span>Ctrl</span>+<span>Enter</span><br>'+
' :置換を確定する<br><span>UNDO</span> / <span>Esc</span>'+
'<br> :全て元に戻す'; }}
}}
p_flag=0; // 0=検索文字 未確定
search_box.focus();
search_box.onkeydown=function(event){ // 🔽 検索ツール操作の開始点
if(event.keyCode==13){
if(p_flag==0){
event.preventDefault();
search_word=search_box.value; // 🟥 検索文字取得
native_hk=-1; // 初期化🅿
get_search();
result_box_disp(); }
else if(p_flag==1){
event.preventDefault();
if(search_box.value==search_word){
if(caution==1 && t_flag>0 ){
s_6.style.display='inline-block';
arg_t_or_h=0; } // caution文字チェックに該当し「置換」は不可
else{
replace_box.style.display='inline-block';
replace_box.focus();
arg_t_or_h=0;
p_flag=2;
replace_process(); }} // 巡回ループを抜けて 置換入力へ
else{
iframe_body.innerHTML=buffer; // highlight を抜ける時はリセット ⏹
search_word=search_box.value; // 🟥 検索文字取得 変更
native_hk=-1; // 初期化🅿
result_box.textContent='⏎';
caution_reset();
s_1.style.display='none';
arg_t_or_h=0;
p_flag=0; // 巡回ループを抜けて 検索文字 未確定へ
search_box.dispatchEvent( new KeyboardEvent( "keydown", {keyCode: 13})); }}}
if(event.keyCode==9){ //「Tab」で置換入力へ
if(p_flag==0){
event.preventDefault(); } //「Tab」で入力枠外に出るのを抑止
else if(p_flag==1){
event.preventDefault();
if(caution==1 && t_flag>0 ){
s_6.style.display='inline-block';
arg_t_or_h=0; } // caution文字チェックに該当し「置換」は不可
else{
replace_box.style.display='inline-block';
replace_box.focus();
arg_t_or_h=0;
p_flag=2;
replace_process(); }}} // 巡回ループを抜ける
if(event.keyCode==27 ){ //「Esc」
if(p_flag==1 && t_flag==1){ //「巡回」表示をリセット
event.preventDefault();
result_box.textContent='T:'+count_t+'│-';
iframe_body.innerHTML=buffer; // highlight を抜ける時はリセット ⏹
caution_reset();
arg_t_or_h=0;
p_flag=0; }}
} // search_box.onkeydown
search_box.onchange=function(){ //「Enter」を押さず移動した場合は検索語を再表示
if(search_box.value!==search_word){
search_box.style.outline='2px solid #2196f3';
search_box.style.outlineOffset='-3px';
setTimeout(()=>{
search_box.style.outline='none';
search_box.value=search_word; }, 500); }}
s_7.onclick=function(){
s_container_remove(); } // s_7.onclick 「✖ 閉じる」で終了
function result_box_disp(){
t_flag=0; // t_flag リセット
arg_t_or_h=0; // リセット
search_box.disabled=false;
replace_box.disabled=false;
s_1.style.display='inline-block';
s_1.style.boxShadow='none';
s_2.style.display='none';
s_3.style.display='none';
replace_box.style.display='none';
replace_box.value='';
if(count_t!=0 && count_h==0){
s_1.textContent='TEXT処理';
t_flag=1; // TEXT処理
p_flag=1; // 1=検索文字確定 処理開始
t_process(); // 🔳 RegExp
next(hk); }
if(count_t!=0 && count_h!=0){
p_flag=1; // 1=検索文字確定 処理開始
arg_t_or_h=1; // この場合だけフラグを立てる
t_h_select(); } // TEXT・HTML処理選択
if(count_t==0 && count_h!=0){
result_box.textContent='H:'+count_h;
s_1.textContent='HTML処理';
s_1.style.color='#000';
t_flag=0;
p_flag=1; // 1=検索文字確定 処理開始
replace_process(); }
if(count_t==0 && count_h==0){
result_box.textContent='T:'+count_t+' H:'+count_h;
s_1.textContent=' - - - ';
s_1.style.color='#000';
p_flag=0; } // 0=検索文字未確定 検索前
function t_h_select(){
if(t_or_h==1){
t_flag=1; // TEXT処理
result_box.textContent='T:'+count_t+'│-';
s_1.innerHTML='TEXT処理 <span>HTML</span>';
s_1.style.boxShadow='inset -56px 0 0 0 #cfd8dc';
t_process();
next(hk); }
else{
t_flag=0; // HTML処理
result_box.textContent='H:'+count_h;
s_1.innerHTML='<span>TEXT</span> HTML処理';
s_1.style.boxShadow='inset 54px 0 0 0 #cfd8dc';
replace_process(); }
s_1.onclick=function(){
search_box.focus();
native_hk=-1; // 初期化🅿
iframe_body.innerHTML=buffer; // highlight を抜ける時はリセット ⏹
get_search();
if(t_or_h==1){
t_or_h=0; // HTML処理を選択
t_flag=0;
s_1.innerHTML='<span>TEXT</span> HTML処理';
s_1.style.boxShadow='inset 54px 0 0 0 #cfd8dc';
result_box.textContent='H:'+count_h;
replace_process(); }
else{
t_or_h=1; // TEXT処理を選択
t_flag=1;
s_1.innerHTML='TEXT処理 <span>HTML</span>';
s_1.style.boxShadow='inset -56px 0 0 0 #cfd8dc';
result_box.textContent='T:'+count_t+'│-';
t_process();
next(hk); }}}
search_box.onblur=function(){ //「検索枠」が focusを無くしたらリセット
setTimeout( ()=>{
if(replace_box.style.display=='none'){ //「置換」へ移動とT/H操作は除外
stop_out(); }}, 10); }
replace_box.onblur=function(){ //「置換枠」が focusを無くしたらリセット
setTimeout( ()=>{
if(p_flag!=3){ //「置換チェック画面」へ移行は除外
stop_out(); }}, 10); }
function stop_out(){
if(arg_t_or_h==1){
setTimeout(()=>{
if(search_box!=document.activeElement){
arg_t_or_h=0;
stop_out(); }}, 400); }
else{
if(p_flag==1 || p_flag==2){ // 1=検索文字入力 2=置換文字入力
if(t_flag>0){
result_box.textContent='T:'+count_t+'│-'; }
else{
result_box.textContent='H:'+count_h; }
s_1.style.display='none';
replace_box.style.display='none';
replace_box.value='';
iframe_body.innerHTML=buffer; // highlight を抜ける時はリセット ⏹
native_hk=-1; // 初期化🅿
caution_reset();
arg_t_or_h=0;
t_flag=0;
p_flag=0; }}}
} // result_box_disp()
function replace_process(){ // 置換処理全般
replace_box.focus();
replace_box.onkeydown=function(event){ // 🔽 置換操作の開始点
if(event.keyCode==13 && event.ctrlKey==false){
event.preventDefault();
replace_word=replace_box.value; // 🟥 置換文字取得
if(t_flag>0){
t2_process(); } // 🔳 RegExp
else{
h_process(); } // 🔳 RegExp
js_cover.style.display='block';
cke_1_contents.style.zIndex='11';
iframe_body.contentEditable='false'; // 編集不可にする
search_box.disabled=true;
replace_box.disabled=true;
s_2.style.display='inline-block';
s_3.style.display='inline-block';
add2_mu_style();
if(t_flag>0 && p_flag==2){
s_4.textContent='一括 選択';
s_4.style.display='inline-block';
s_4.style.boxShadow='inset -45px 0 0 0 #b0bec5';
s_5.style.display='inline-block';
disp_help();
replace_box.blur();
next(hk); } //「一括置換」の「巡回表示」
s_7.style.display='none';
if(sr_data[0]==0 && t_flag>0){ // HTML処理の時は非表示
s_8.style.background='#ddd';
s_8.style.display='inline-block'; }
else if(sr_data[0]==1 && t_flag>0){ // HTML処理の時は非表示
s_8.style.background='#333';
s_8.style.display='inline-block'; }
p_flag=3; } // 3=置換処理
if(event.keyCode==9 || event.keyCode==27){ //「Tab」「Esc」で処理前に戻る
event.preventDefault();
result_box.textContent=' ';
s_1.style.display='none';
replace_box.style.display='none';
replace_box.value='';
if(t_flag==1){
iframe_body.innerHTML=buffer; } // 置換処理をUNDO ⏹
search_box.focus();
p_flag=0; }} // 0=検索文字 未確定
s_2.onclick=function(){ //「OK」ボタンで一括置換確定
js_cover_remove();
delete_mu(); // muタグを削除
result_box.textContent=' '; // 検索結果は変更される
s_1.style.display='none';
replace_box.style.display='none';
replace_box.value='';
s_8.style.display='none';
reset_mu_style();
if(t_flag>0){
t_flag=0;
native_hk=-1; } // 初期化🅿
search_box.focus();
p_flag=0; } // 0=検索文字 未確定
s_3.onclick=function(){ //「UNDO」ボタン
js_cover_remove();
iframe_body.innerHTML=buffer; // 置換処理をUNDO ⏹
get_search();
s_8.style.display='none';
reset_mu_style();
if(t_flag>0){
t_flag=1;
t_process(); // 🔳 RegExp
next(hk); } // UNDO時は「一括置換」の「巡回表示」
replace_box.focus();
p_flag=2; } // 2=検索文字確定 置換文字入力 処理選択
s_4.onclick=function(){ //「一括・選択」ボタン
if(t_flag==1){
t_flag=2; //「選択置換」に変更
s_4.style.boxShadow='inset 45px 0 0 0 #b0bec5';
select_replace(); } //「選択置換」を実行
else if(t_flag==2){
t_flag=1; //「一括置換」に変更
s_4.style.boxShadow='inset -45px 0 0 0 #b0bec5';
all_replace(); }
disp_help(); }
s_4.addEventListener('mouseover', ()=>{
if(t_flag==2){
s_5.innerHTML='「一括」 に切換えると<br>選択した置換の指定は'+
'<br>全てリセットされます'; }}, false);
s_4.addEventListener('mouseleave', ()=>{
disp_help(); }, false);
function disp_help(){
if(t_flag==1){
s_5.innerHTML='<span>⇦</span><span>⇧</span><span>⇩</span>'+
'<span>⇨</span>:移動<br><span>OK</span> / <span>Ctrl</span>'+
'+<span>Enter</span><br> :置換を確定する<br>'+
'<span>UNDO</span> / <span>Esc</span><br> :全て元に戻す'; }
else if(t_flag==2){
s_5.innerHTML='<span>⇦</span><span>⇧</span><span>⇩</span>'+
'<span>⇨</span>:移動<br><span>Space</span>:設定 / 解除<br>'+
'<span>OK</span> / <span>Ctrl</span>+<span>Enter</span><br>'+
' :置換を確定する<br><span>UNDO</span> / <span>Esc</span>'+
'<br> :全て元に戻す'; }}
function all_replace(){ //「一括置換処理」
t2_process(); // 🔳 RegExp
next(hk); }
function select_replace(){ //「選択置換処理」
iframe_body.innerHTML=buffer; // 置換処理を一旦デフォルトに戻す ⏹
get_search();
t_process(); // 🔳 RegExp
next(hk); }
s_8.onclick=function(){ //「C」連続処理モードボタン
if(sr_data[0]==0){
let result=window.confirm(
" ⬛ 連続処理モードをONにします\n"+
" 「OK」:現在の「検索文字」「置換文字」「一括/選択」で開始\n"+
" 「CANCEL」:以前の「検索文字」「置換文字」「一括/選択」で再開");
if(result){
sr_data[1]=t_flag;
sr_data[2]=search_word;
sr_data[3]=replace_word; }
sr_data[0]=1;
s_8.style.background='#333'; }
else{
sr_data[0]=0;
s_8.style.background='#ddd'; }
let write_json=JSON.stringify(sr_data);
localStorage.setItem('sr_editor', write_json); } // ローカルストレージ 保存名
} // replace_process()
} // #s_container が無い場合「Ctrl+F12」で開始
} // search_replace()
function next(hk){ //「巡回表示」「選択置換」コード
let mark=iframe_body.querySelectorAll('mu');
if(native_hk!=-1){ // 基本的に前回のインデックスを再現🅿
hk=native_hk; }
else if(native_hk==-1 || !native_hk){ // 初期インデックス生成🅿
if(mark.length==1){ hk=0; } // 1個なら即決定
else{
let near_n; // 中央後方の要素のインデックス
let editor_hight=editor_iframe.clientHeight; // 編集枠の高さ
for(let k=1; k<mark.length; k++){ // スクロール位置中央を越えるmark[k]を取得
if(mark[k].getBoundingClientRect().top>editor_hight/2){
near_n=k;
break; }}
if(!near_n){ // スクロール位置中央より後方にmark[k]がない場合
hk=mark.length-1; }
else{ // 直前の mark[k]と比較して、近い方を採る
if(mark[near_n].getBoundingClientRect().top>
editor_hight-mark[near_n-1].getBoundingClientRect().top){
hk=near_n-1; }
else{
hk=near_n; }}}}
view(hk);
try{
mark[hk].classList.add("h"); } // 最初のハイライト色を変更
catch(e){ ; }
result_box.textContent='T:'+count_t+'│'+(hk+1);
document.addEventListener("keydown", check_key); // documentは先に指定
iframe_doc=editor_iframe.contentWindow.document;
if(iframe_doc){
iframe_doc.addEventListener("keydown", check_key); }// iframeは後に指定
function check_key(event){
if(event.keyCode==38){ //「↑」
if(p_flag>0 && t_flag>0){
event.preventDefault();
back(); }}
if(event.keyCode==37){ //「←」
if(p_flag==3 && t_flag>0){
event.preventDefault();
back(); }}
if(event.keyCode==40){ //「↓」
if(p_flag>0 && t_flag>0){
event.preventDefault();
forward() }}
if(event.keyCode==39){ //「→」
if(p_flag==3 && t_flag>0){
event.preventDefault();
forward() }}
if(event.keyCode==32){ //「Space」で個別に置換の設定/解除
if(p_flag==3 && t_flag==2){
event.preventDefault();
if(mark[hk].textContent==search_word){
mark[hk].textContent=replace_word; }
else if(mark[hk].textContent==replace_word){
mark[hk].textContent=search_word; }}}
native_hk=hk;
function back(){
if(hk>0){ // 標準のハイライト色に戻す
mark[hk].classList.remove("h");
hk-=1; }
else if(hk==0){
hk=0; }
result_box.textContent='T:'+count_t+'│'+(hk+1);
try{
mark[hk].classList.add("h"); }
catch(e){ ; }
view(hk); }
function forward(){
if(hk<mark.length-1){ // 標準のハイライト色に戻す
mark[hk].classList.remove("h");
hk+=1; }
else if(hk==mark.length-1){
hk=mark.length-1; }
result_box.textContent='T:'+count_t+'│'+(hk+1);
try{
mark[hk].classList.add("h"); }
catch(e){ ; }
view(hk); }
}} // next() インデックス取得🅿
function view(hk){
let l_body=document.querySelector('body.l-body');
let mark=iframe_body.querySelectorAll('mu');
try{
mark[hk].scrollIntoView({block: "center"});
iframe_html.scrollBy(0, -12); } // -1~-24 -12がクリープを無くす最適値
catch(e){ ; }
l_body.scrollIntoView(); }
// ●コード前半
// ●コード後半
function get_search(){
search_word_es=escapeRegExp(search_word); // 🔳 RegExp
editor_iframe=document.querySelector('.cke_wysiwyg_frame'); // ここで取得
if(editor_iframe){ //「通常表示」が実行条件
iframe_doc=editor_iframe.contentWindow.document;
iframe_body=iframe_doc.querySelector('.cke_editable');
buffer=iframe_body.innerHTML; // ハイライト表示のためソースコードを保存 🟦
buffer_arr=[]; // 切分けたソースコードを入れる配列
let sa=0;
let sb=0;
let sc=0;
let result_t;
let result_h;
let n=buffer.split('<').length-1; // 開始・終結を含む全タグ数を取得
for(let k=0; k<n; k++){
sb=buffer.indexOf('>', sa);
buffer_arr.push(buffer.slice(sa, sb+1)); //タグ括弧内 1個を配列に収納
sc=buffer.indexOf('<', sb+1);
if(sc==-1){ break; } // 文書の末尾で後がない場合に終了
else{
buffer_arr.push(buffer.slice(sb+1, sc)); //タグ括弧外 1個を配列に収納
sa=sc; }}
avoid=[]; //「styleタグ」のインデックスを記録
for(let i=0; i<n; i++){
if(buffer_arr[i*2].match(new RegExp('<style'))){
avoid.push(i*2); }}
count_t=0; // テキストノードのヒット数
for(let i=0; i<n; i++){ //配列の奇数インデックスはタグ括弧外(TEXT)
if(buffer_arr[i*2+1]){
result_t=buffer_arr[i*2+1].match(new RegExp(search_word_es, 'g')); // 🔳 RegExp
if(result_t){
count_t+=result_t.length; }}}
count_h=0; // HTMLコードのヒット数
for(let i=0; i<n; i++){ //配列の偶数インデックスはタグ括弧内(HTMLコード)
result_h=buffer_arr[i*2].match(new RegExp(search_word_es, 'g')); // 🔳 RegExp
if(result_h){
count_h+=result_h.length; }}
caution_ck(); //「no-break space」「文字実体参照」の可能性をチェック
}} // get_search()
function caution_ck(){
caution=0;
document.querySelector('.c_nb').style.display='none';
document.querySelector('.c_lt').style.display='none';
document.querySelector('.c_gt').style.display='none';
document.querySelector('.c_am').style.display='none';
if(' '.match(new RegExp(search_word_es))){
if(buffer.match(new RegExp(/ /))){
document.querySelector('.c_nb').style.display='inline';
caution=1; }}
if('<'.match(new RegExp(search_word_es))){
if(buffer.match(new RegExp(/</))){
document.querySelector('.c_lt').style.display='inline';
caution=1; }}
if('>'.match(new RegExp(search_word_es))){
if(buffer.match(new RegExp(/>/))){
document.querySelector('.c_gt').style.display='inline';
caution=1; }}
if('&'.match(new RegExp(search_word_es))){
if(buffer.match(new RegExp(/&/))){
document.querySelector('.c_am').style.display='inline';
caution=1; }}}
function caution_reset(){
s_6.style.display='none'; }
function t_process(){
let rep_word='<mu>'+ search_word +'</mu>';
t_replace(rep_word); }
function t2_process(){
let rep_word=replace_word;
t_replace(rep_word); }
function t_replace(rep_word){
let n=buffer.split('<').length-1; // 開始・終結を含む全タグ数を取得
for(let i=0; i<n; i++){ //配列の奇数インデックスはタグ括弧外(TEXT)
let pass=1;
for(let j=0; j<avoid.length; j++){
if(avoid[j]==i*2){
pass=0;
break; }}
if(pass!=0 && buffer_arr[i*2+1]){
buffer_arr[i*2+1]=buffer_arr[i*2+1].replace(new RegExp(search_word_es, 'g'), rep_word); }} // 🔳 RegExp
iframe_body.innerHTML=buffer_arr.join(''); }
function h_process(){
let rep_word=replace_word;
let n=buffer.split('<').length-1; // 開始・終結を含む全タグ数を取得
for(let i=0; i<n; i++){ //配列の偶数インデックスはタグ括弧内(HTMLコード)
if(buffer_arr[i*2]){
buffer_arr[i*2]=buffer_arr[i*2].replace(new RegExp(search_word_es, 'g'), rep_word); }} // 🔳 RegExp
iframe_body.innerHTML=buffer_arr.join(''); }
function add_mu_style(){
editor_iframe=document.querySelector('.cke_wysiwyg_frame');
if(editor_iframe){ //「通常表示」の場合
iframe_doc=editor_iframe.contentWindow.document;
iframe_html=iframe_doc.querySelector('html');
let css_iframe=
'.cke_editable mu { background: #ffcc00; } '+ // ハイライト muタグ背景色⭕
'.cke_editable mu.h { background: #85ff00; }'; // フォーカス muタグ背景色⭕
let style_tag_iframe=iframe_doc.createElement("style");
style_tag_iframe.type="text/css";
style_tag_iframe.setAttribute("class", "ep");
style_tag_iframe.appendChild(document.createTextNode(css_iframe));
if(iframe_html.querySelector('.ep')){
iframe_html.querySelector('.ep').remove(); }
iframe_html.appendChild(style_tag_iframe); }}
function add2_mu_style(){
if(replace_word==''){
replace_box.setAttribute('placeholder', " 🟦 削除モード");
replace_box.style.border='2px solid #009688';
replace_box.style.background='#090907';
replace_box.style.filter='invert(1)';
if(t_flag>0){
if(iframe_html.querySelector('.ep')){ // ハイライト・フォーカス muタグ「削除モード」⭕
iframe_html.querySelector('.ep').textContent=
'.cke_editable mu { box-shadow: 0 0 0 2px #ffcc00; background: #ffcc00; } '+
'.cke_editable mu.h { filter: opacity(1); '+
'box-shadow: 0 0 0 2px red; background: #fce4ec; }'; }}}}
function reset_mu_style(){
replace_box.setAttribute('placeholder', " 置換文字");
replace_box.style.border='';
replace_box.style.background='';
replace_box.style.filter='none';
if(t_flag>0){
if(iframe_html.querySelector('.ep')){ // ハイライト・フォーカス muタグ デフォルト ⭕
iframe_html.querySelector('.ep').textContent=
'.cke_editable mu { background: #ffcc00; } '+
'.cke_editable mu.h { background: #85ff00; }'; }}}
function delete_mu(){
editor_iframe=document.querySelector('.cke_wysiwyg_frame');
if(editor_iframe){ //「通常表示」の場合
iframe_doc=editor_iframe.contentWindow.document;
iframe_body=iframe_doc.querySelector('.cke_editable');
if(iframe_body){
let mark=iframe_body.querySelectorAll('mu');
if(mark.length!=0){
iframe_body.innerHTML=
iframe_body.innerHTML.replace(new RegExp('<mu.*?>', 'g'), ''); }}}} // 🔳 RegExp
function escapeRegExp(string){
let reRegExp=/[\\^$.*+?()[\]{}|]/g;
let reHasRegExp=new RegExp(reRegExp.source);
return (string && reHasRegExp.test(string))
? string.replace(reRegExp, '\\$&')
: string; }
}
〔追記〕2021.12.23
ヒット文字列をハイライトするための「styleタグ」を、ショートカットでツールをONにするまで書き込まない仕様に変更。(修正版 ver. 3.0)
●「S-R in Editor ⭐」の最新バージョンは、以下から探せます。


