「pre枠」の整形機能 「To Space」
コード表記の「pre枠」内のペースト操作で大量の「 」(no-break space)が生成されます。 この原因はインデントの半角空白が、ペースト操作時に「 」に変化するためで、これはアメブロ編集画面のエディターの仕様です。
下は「pre枠」にコードをペーストした例で、青背景の部分が「 」です。
「 」は記事上の表示は半角空白と同じですが、実際は半角6文字分のデータとなり、記事の文字数制限超過の原因になります。 そこで「 」を半角空白に置き換え、文字数を減らすためのツール「To Space」が必要になりました。
●「Ctrl + F9」を押して「BR Checker + To Space ⭐」を起動
→ 編集画面の上に赤いツールの表示が出ます。
● 「 」の置換えを行う「pre枠」を右クリック
→ ダイアログが表示され、OKを押すと整形処理が実行されます。
「p要素」の段分けと 「br」による改行
文字列中で「Enter」を押すと段落分けの指定となり「p要素」が発生します。 一方、文字列中で「Shift + Enter」を押すと「brタグ」が書き込まれます。 表示上は変わらないのですが、文書の複数段落のコピー時に、両者で結果が変わる場合があります。
これも普通は気にする問題ではありませんが、「br」による改行をブログ記事の標準の「段落分け」に置き換える機能が「BR Checker」です。
●「Ctrl + F9」で「BR Checker + To Space ⭐」を起動
→ 本文の「brタグ」の位置に「▼」が表示されます。
●「Ctrl + F10」を押すと、段落への自動置換が実行されます
「shell値」は処理中断があると意味を持ちますが、気にする必要はありません。
この整形機能の詳細は、以下のページの最後を参照ください。
文書の文字数カウンター
文字数制限が問題になる場合、正確な文字数を判断できる文字数カウンターが必要になります。「BR Checker + To Space ⭐」には、文字数カウンターを搭載していますが、このカウンターはその目的で作成したものです。
カウント値は、標準カウンターと違い自動的には更新されません。
●「Ctrl + F9」または「Ctrl + F10」を押した時にカウント値を更新
● カウント値は半角文字数
半角文字数で 約58000~60000文字が、記事文字数の上限です。 実際の上限値は、記述内容で微妙に変化しますが、それを可能な範囲で補正しています。
下は制限文字数のテストで、あと1文字を追加すると文字限界で保存できません。
ツールのカウンターは「約98%」、標準カウンター×2は「約120%」の表示ですから、標準カウンターはかなり大雑把です。
「アメンバー限定公開」ボタン廃止に伴う更新
編集画面の投稿ボタンで、 「アメンバー限定公開」ボタンが廃止され、このツールがスクリプトエラーを生じました。 エラーは問題を生じないレベルでしたが、対策更新を行い抑止しました。
また、起動表示帯の配置コードを改善し、編集画面の幅で表示する様にしました。
〔追記〕2020.11.09
Firefoxで「HTML編集画面」での編集終了にコードが反応ができない問題に対処して、「mousedown」→「click」の変更をしました。 以下の「ver. 2.2f」は、この対策を更新した版です。
「BR Checker + To Space (nbsp) ⭐」ver. 2.2
◎このツールは Chrome / 新Edge / Firefox の拡張機能「Tampermonkey」上で動作を確認しています。「Tampermonkey」の導入と初期設定などについては、以下を参照ください。
◎以下のコードを「Tampermonkey」の新規作成画面にコピー&ペーストして保存すると、最新版エディタの「編集画面」でこのツールを使用する事が出来ます。
〔コピー方法〕 右サイドバーの マークのボタンを1度押してください。
コード枠内の右クリック ➔ コード全体の選択 ➔ コピー操作 が可能になります。
〔 BR Checker + To Space (nbsp) ⭐ 〕 ver. 2.2f
// ==UserScript== // @name BR Checker + To Space (nbsp) ⭐ // @namespace http://tampermonkey.net/ // @version 2.2f // @description Blogの書式整形ツール・文字数カウンター 統合版 // @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 marked1=0; // BR Checker 起動・非起動の指標 let marked2=0; // To Space (nbsp) 起動・非起動の指標 let target; let editor_iframe; let iframe_doc; let iframe_body; let pre_box; // PRE枠のコンテナブロック target=document.getElementById('cke_1_contents'); // 監視 target let monitor=new MutationObserver(catch_key); monitor.observe(target, {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'; } let send; editor_iframe=document.querySelector('.cke_wysiwyg_frame'); if(editor_iframe){ // WYSIWYG表示が実行条件 iframe_doc=editor_iframe.contentWindow.document; if(marked1==1){ sign(); } if(marked2==1){ active(); } document.addEventListener("keydown", check_key); iframe_doc.addEventListener("keydown", check_key); function check_key(event){ let gate=-1; if(event.ctrlKey==true){ if(event.keyCode==120){ event.preventDefault(); send=120; gate=1; }} // ショートカット「Ctrl+F9」 if(event.ctrlKey==true){ if(event.keyCode==121){ event.preventDefault(); send=121; gate=1; }} // ショートカット「Ctrl+F10」 if(editor_iframe){ if(gate==1){ event.stopImmediatePropagation(); event.preventDefault(); set_task(send); }}} brfore_end(); }} // catch_key function set_task(send){ counter(); if(send==120){ //「Ctrl+F9」 BR Checker / To Space (nbsp) スイッチ if(marked1==0){ marked1=1; marked2=1; sign(); active(); } // BR Checker / To Space (nbsp) をON else if(marked1==1){ marked1=0; marked2=0; sign_clear(); disable(); }} // BR Checker / To Space (nbsp) をOFF if(send==121){ //「Ctrl+F10」BR Rewrite スイッチ marked1=1; sign_clear(); rewrite(); } } // set_task // ********** BR Rewrite Functions ************** function rewrite(){ let br_tag; let br_rewrite; let o_tag; let native_line; first_rw(go_back); function first_rw(){ let wysiwyg=iframe_doc.querySelector('html'); native_line=wysiwyg.scrollTop; // 通常表示のスクロール位置を記録 br_tag=iframe_doc.querySelectorAll('br'); for(let i=0; i < br_tag.length; i++){ if(br_tag[i].parentNode.tagName=="P" && br_tag[i].parentNode.childNodes.length !=1){ br_rewrite=iframe_doc.createElement("p"); br_rewrite.appendChild(iframe_doc.createTextNode("")); br_rewrite.setAttribute("class", "brs"); br_tag[i].parentNode.replaceChild(br_rewrite, br_tag[i]); }} go_back(); } function go_back(){ document.querySelector('button[data-mode="source"]').click(); // HTML表示に移動 let interval0=setInterval(find_mirror, 10); function find_mirror(){ let CodeMirror=document.querySelector('.CodeMirror'); if(CodeMirror){ clearInterval(interval0); document.querySelector('button[data-mode="wysiwyg"]').click(); go_back2(); }}} function go_back2(){ let interval1=setInterval(find_iframe, 10); function find_iframe(){ let editor_iframe=document.querySelector('.cke_wysiwyg_frame'); if(editor_iframe){ iframe_doc=editor_iframe.contentWindow.document; if(iframe_doc){ clearInterval(interval1); setTimeout( function(){ second_rw(); }, 400); setTimeout( function(){ sign(); }, 800); }}}} function second_rw(){ o_tag=iframe_doc.querySelectorAll('.brs'); if(o_tag.length >0){ for(let i=0; i < o_tag.length; i++){ if(o_tag[i].nextElementSibling==o_tag[i+1] ){ o_tag[i].classList.remove('brs'); } // 2連 .brs 生成の場合は先頭側class名を削除 else{ o_tag[i].remove(); }}} // 単体の .brs の場合は削除(上の後方側も含む) let wysiwyg=iframe_doc.querySelector('html'); wysiwyg.scrollTop=native_line; }} // 記録された通常表示のスクロール位置に移動 // ********** BR Checker Functions ************** function sign(){ let br_tag; let br_mark; let i_tag; let o_tag; let c_br=0; let c_br_span; let c_brrw=0; let c_brrw_span; let css_inn= '.brm { position: absolute } '+ 'i.brm:before { content: "▼"; color: red; margin-left: -2px; font-style: normal } '+ 'p.brs:before { content: "▼"; color: #008fff; margin-left: -2px; font-style: normal }'; let style_inn=iframe_doc.createElement('style'); // iframe内の BRのデザインを指定 style_inn.setAttribute("id", "style_inn"); style_inn.insertAdjacentHTML('afterbegin', css_inn); let html=iframe_doc.getElementsByTagName('html')[0]; if(!html.querySelector('#style_inn')){ html.appendChild(style_inn); } i_tag=iframe_doc.querySelectorAll('.brm'); if(i_tag.length >0){ for(let i=0; i < i_tag.length; i++){ i_tag[i].remove(); }} // BRマーク削除してリセット o_tag=iframe_doc.querySelectorAll('.brs'); // 処理時の brshell の処理残りをカウント確認 if(o_tag.length >0){ c_brrw=o_tag.length; } else{ c_brrw=0; } br_tag=iframe_doc.querySelectorAll('br'); for(let i=0; i < br_tag.length; i++){ if(br_tag[i].parentNode.tagName=="P" && br_tag[i].parentNode.childNodes.length !=1){ c_br +=1; br_mark=iframe_doc.createElement("i"); br_mark.appendChild(iframe_doc.createTextNode("")); // 空白文字 br_mark.setAttribute("class", "brm"); br_tag[i].parentNode.insertBefore(br_mark, br_tag[i]); } else{ continue; }} // BRマーク表示 let css_ext= '#cke_1_contents { display: flex; flex-direction: column; } '+ '.disp_line { display: inline-block; margin: 0 0 5px -14px; padding: 4px 0 2px; '+ 'font-size: 16px; color: #fff; background: red; align-self: center; }'; let disp=document.createElement("span"); disp.setAttribute("class", "disp_line"); disp.appendChild(document.createTextNode(" ▼ BR Checker count : ")); c_br_span=document.createElement("span"); c_br_span.setAttribute("style", "font-weight: bold"); c_br_span.appendChild(document.createTextNode(c_br)); disp.appendChild(c_br_span); disp.appendChild(document.createTextNode(" / shell:")); c_brrw_span=document.createElement("span"); c_brrw_span.setAttribute("style", "font-weight: bold; padding-right: 2em"); c_brrw_span.appendChild(document.createTextNode(c_brrw)); disp.appendChild(c_brrw_span); if(marked2==1){ disp.appendChild(document.createTextNode(" ■ To Space (nbsp) ")); } monitor.disconnect(); // MutationObserverを BR Checker 起動表示に反応させない let style_ext=document.createElement('style'); // disp_line のデザインを指定 style_ext.setAttribute("id", "style_ext"); style_ext.insertAdjacentHTML('afterbegin', css_ext); if(!target.querySelector('#style_ext')){ target.appendChild(style_ext); } let body_width; iframe_body=iframe_doc.querySelector('body.cke_editable'); if(iframe_body){ body_width=iframe_body.style.width; if(body_width){ disp.style.width=body_width; }} if(target.querySelector('.disp_line')){ target.querySelector('.disp_line').remove(); target.insertBefore(disp, editor_iframe); } else{ target.insertBefore(disp, editor_iframe); } monitor.observe(target, {childList: true}); } // BR Checker 起動表示 function sign_clear(){ let i_tag; editor_iframe=document.querySelector('.cke_wysiwyg_frame'); iframe_doc=editor_iframe.contentWindow.document; if(target.querySelector('.disp_line')){ target.querySelector('.disp_line').remove(); } // BR Checker 起動表示を削除 i_tag=iframe_doc.querySelectorAll('.brm'); if(i_tag.length >0){ for(let i=0; i < i_tag.length; i++){ i_tag[i].remove(); }}} // BRマーク削除 // ********** To Space (nbsp) Functions ************** function active(){ let buffer; let nbsp; let a_count; iframe_doc=editor_iframe.contentWindow.document; if(iframe_doc){ //「通常表示」が動作条件 iframe_body=iframe_doc.querySelector('body.cke_editable'); if(iframe_body){ pre_box=iframe_body.querySelectorAll('div'); buffer=Array(pre_box.length); for(let i=0; i<pre_box.length; i++){ select_pre_box(i); } function select_pre_box(i){ pre_box[i].oncontextmenu=function(){ if(pre_box[i].firstChild.tagName=='PRE'){ select_box(select_do); return false; function select_box(select_do){ boxshadow(); setTimeout( select_do, 100); } function boxshadow(){ pre_box[i].style.outline='2px solid #03a9f4'; } function select_do(){ start_select(i); }}}} function start_select(i){ let regex=new RegExp('\u00A0', 'g'); let nbsp=pre_box[i].innerText.match(regex); let pa_count=pre_box[i].getElementsByTagName("a"); if(buffer[i] !=null){ let ok=confirm(" ❎ 変換前に戻しますか?"); if(ok){ pre_box[i].firstChild.innerHTML=buffer[i]; buffer[i]=null; pre_box[i].style.outline='none'; }} else{ 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){ to_space(i); } else{ pre_box[i].style.outline='none'; }} else{ ; }}} function to_space(i){ a_count=0; buffer[i]=pre_box[i].firstChild.innerHTML; // 選択pre枠のデータバックアップ let child_node=pre_box[i].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){ pre_box[i].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=pre_box[i].innerText.match(regex); if(nbsp !=null){ alert("❌ 変換できなかった「 」の数 : " + nbsp.length );} if(a_count !=0){ alert("❌ 置換えが出来ない「a要素」 : " + a_count); }}}}} function disable(){ for(let i=0; i<pre_box.length; i++){ if(pre_box[i].firstChild.tagName=='PRE'){ pre_box[i].style.outline='none'; }} // 処理済枠の outline を削除 pre_box=[]; }// 動作の無効化 // ********** Before End Functions ************** function brfore_end(){ var submitButton=document.querySelectorAll('.js-submitButton'); if(editor_iframe !=null){ //「通常表示」編集画面が実行条件 submitButton[0].addEventListener("click", all_clear, false); submitButton[1].addEventListener("click", all_clear, false); } function all_clear(){ let o_tag=iframe_doc.querySelectorAll('.brs'); // 処理時の brshell の処理残りをカウント確認 if(o_tag.length >0){ alert("⛔ BR削除処理が不完全です BR-Shell数:" + o_tag.length +"\n\n" + " BR削除「Ctrl + F10」 を再実行してください"); event.stopImmediatePropagation(); event.preventDefault(); } else{ let i_tag; editor_iframe=document.querySelector('.cke_wysiwyg_frame'); iframe_doc=editor_iframe.contentWindow.document; i_tag=iframe_doc.querySelectorAll('.brm'); if(i_tag.length >=1){ for(let i=0; i < i_tag.length; i++){ i_tag[i].remove(); }} // マーク削除 let pre_box; iframe_body=iframe_doc.querySelector('body.cke_editable'); pre_box=iframe_body.querySelectorAll('div'); for(let i=0; i<pre_box.length; i++){ if(pre_box[i].firstChild.tagName=='PRE'){ pre_box[i].style.outline='none'; }}}}}// 処理済枠の outline を削除 // ********** Editor Counter ************** function counter(){ let html_source; let count_display; let chr_count; iframe_doc=editor_iframe.contentWindow.document; if(iframe_doc){ //「通常表示」が動作条件 iframe_body=iframe_doc.querySelector('.cke_editable'); iframe_body.querySelectorAll('a').forEach((link) => { link.removeAttribute('data-cke-saved-href'); }); //「a要素」の「data-cke-saved」補正 iframe_body.querySelectorAll('img').forEach((link) => { link.removeAttribute('data-cke-saved-src'); }); //「img要素」の「data-cke-saved」補正 html_source=iframe_body.innerHTML; // HTMLソースコードを取得 chr_count=get_count(html_source); function get_count(str){ let result=0; for(let i=0; i<str.length; i++){ let chr=str.charCodeAt(i); if((chr>=0x00 && chr<0x81) || (chr===0xf8f0) || (chr>=0xff61 && chr<0xffa0) || (chr>=0xf8f1 && chr<0xf8f4)){ result += 1; } //半角文字の場合は1を加算 else{ result += 2; }} //それ以外の文字の場合は2を加算 return result; } // 以下は、挿入要素ごとの補正 let blockquote_tag=iframe_body.querySelectorAll('blockquote'); let div_tag=iframe_body.querySelectorAll('div'); let p_tag=iframe_body.querySelectorAll('p'); let h2_tag=iframe_body.querySelectorAll('h2'); let h3_tag=iframe_body.querySelectorAll('h3'); let h4_tag=iframe_body.querySelectorAll('h4'); let ul_tag=iframe_body.querySelectorAll('ul'); let ol_tag=iframe_body.querySelectorAll('ol'); let li_tag=iframe_body.querySelectorAll('li'); let pre_tag=iframe_body.querySelectorAll('pre'); let br_tag=iframe_body.querySelectorAll('br'); let iframe_tag=iframe_body.querySelectorAll('iframe'); let table_tag=iframe_body.querySelectorAll('table'); let tr_tag=iframe_body.querySelectorAll('tr'); let td_tag=iframe_body.querySelectorAll('td'); let style_tag=iframe_body.querySelectorAll('style'); let correct= 6*blockquote_tag.length + 2*div_tag.length + 2*p_tag.length + 2*h2_tag.length + 2*h3_tag.length + 2*h4_tag.length + 6*ul_tag.length + 6*ol_tag.length + 3*li_tag.length + 6*pre_tag.length + 2*br_tag.length + 2*iframe_tag.length + 12*table_tag.length + 8*tr_tag.length + 5*td_tag.length + 6*style_tag.length; let wordcount=document.querySelector('.cke_wordcount'); wordcount.style.margin="9px 20px 0 0"; wordcount.style.padding="0 10px"; let path_item=document.querySelector('.cke_path_item'); path_item.style.font="normal 14px Meiryo"; path_item.style.color="#000"; let real_count=document.createElement("span"); real_count.setAttribute("class", "real_count"); real_count.style.font="normal 14px Meiryo"; real_count.style.color="#000"; if(wordcount.querySelector('.real_count')){ wordcount.querySelector('.real_count').remove(); } wordcount.appendChild(real_count); real_count.innerHTML=' 半角: ' + (chr_count+correct); }} } // main()
「BR Checker」「To Space (nbsp)」最新版について
旧いバージョンの JavaScriptツールは、アメーバのページ構成の変更で動作しない場合があり、導入する場合は最新バージョンをお勧めします。
●「BR Checker」は、後に「BR Checker+Outro Style ⭐」に統合されています。 その最新バージョンへのリンクは、以下のページのリンクリストから探せます。
●「To Space (nbsp)」は、後に「PreBox Tools ⭐」に統合されています。 その最新バージョンへのリンクは、以下のページのリンクリストから探せます。